Detect file encoding java

Java: как определить правильную кодировку кодировки потока

Каков наилучший способ программно определить правильную кодировку кодировки входного потока/файла?

Я пробовал использовать следующее:

File in = new File(args[0]); InputStreamReader r = new InputStreamReader(new FileInputStream(in)); System.out.println(r.getEncoding()); 

Но в файле, который, как я знаю, был закодирован с ISO8859_1, приведенный выше код дает ASCII, что неверно, и не позволяет мне корректно отображать содержимое файла обратно на консоль.

Я использовал эту библиотеку, подобную jchardet для обнаружения кодировки в Java:
http://code.google.com/p/juniversalchardet/

Вы не можете определить кодировку произвольного байтового потока. Это характер кодировок. Кодировка означает сопоставление между байтом и его представлением. Поэтому каждая кодировка “может” быть правильной.

Метод getEncoding() вернет кодировку, которая была настроена (прочитайте JavaDoc) для потока. Он не угадает кодировку для вас.

Некоторые потоки сообщают вам, какая кодировка была использована для их создания: XML, HTML. Но не произвольный поток байтов.

В любом случае, вы можете попытаться угадать кодировку самостоятельно, если вам нужно. Каждый язык имеет общую частоту для каждого char. На английском языке char e появляется очень часто, но ê будет появляться очень редко. В потоке ISO-8859-1 обычно нет символов 0x00. Но у потока UTF-16 их много.

Или: вы можете спросить пользователя. Я уже видел приложения, которые представляют вам фрагмент файла в разных кодировках и просят вас выбрать “правильный”.

проверьте это:
http://site.icu-project.org/ (icu4j)
у них есть библиотеки для обнаружения кодировки от IOStream
может быть простым:

BufferedInputStream bis = new BufferedInputStream(input); CharsetDetector cd = new CharsetDetector(); cd.setText(bis); CharsetMatch cm = cd.detect(); if (cm != null) < reader = cm.getReader(); charset = cm.getName(); >else
 org.apache.any23 apache-any23-encoding 1.1  
public static Charset guessCharset(InputStream is) throws IOException
 org.codehaus.guessencoding guessencoding 1.4 jar  
 public static Charset guessCharset2(File file) throws IOException

Вы можете, конечно, проверить файл для определенной кодировки декодировать его с помощью CharsetDecoder и следить за ошибками “неправильный ввод” или “неуправляемый символ”. Конечно, это говорит только о неправильной кодировке; он не говорит вам, правильно ли это. Для этого вам нужна основа для сравнения для оценки декодированных результатов, например. знаете ли вы заранее, если символы ограничены каким-то подмножеством, или текст придерживается какого-то строгого формата? Суть в том, что обнаружение набора символов – это догадки без каких-либо гарантий.

Вышеупомянутые библиотеки – это простые детекторы спецификации, которые, конечно, работают только в том случае, если в начале файла есть спецификация. Взгляните на http://jchardet.sourceforge.net/, который сканирует текст

Я нашел хорошую стороннюю библиотеку, которая может обнаруживать фактическую кодировку:
http://glaforge.free.fr/wiki/index.php?wiki=GuessEncoding

Я не тестировал его широко, но, похоже, он работает.

Если вы не знаете кодировку своих данных, определить ее не так просто, но вы можете попытаться использовать библиотеку чтобы угадать ее. Кроме того, есть аналогичный вопрос.

Для файлов ISO8859_1 нет простого способа отличить их от ASCII. Однако для файлов Unicode обычно можно обнаружить это на основе первых нескольких байтов файла.

К сожалению, по историческим причинам Java не обнаруживает это автоматически. Такие программы, как “Блокнот”, будут проверять спецификацию и использовать соответствующую кодировку. Используя unix или Cygwin, вы можете проверить спецификацию с помощью команды file. Например:

$ file sample2.sql sample2.sql: Unicode text, UTF-16, big-endian 

Для Java я предлагаю вам проверить этот код, который будет определять общие форматы файлов и выбирать правильную кодировку: Как читать файл и автоматически укажите правильную кодировку

 String charset = "ISO-8859-1"; //Default chartset, put whatever you want byte[] fileContent = null; FileInputStream fin = null; //create FileInputStream object fin = new FileInputStream(file.getPath()); /* * Create byte array large enough to hold the content of the file. * Use File.length to determine size of the file in bytes. */ fileContent = new byte[(int) file.length()]; /* * To read content of the file in byte array, use * int read(byte[] byteArray) method of java FileInputStream class. * */ fin.read(fileContent); byte[] data = fileContent; CharsetDetector detector = new CharsetDetector(); detector.setText(data); CharsetMatch cm = detector.detect(); if (cm != null) < int confidence = cm.getConfidence(); System.out.println("Encoding: " + cm.getName() + " - Confidence: " + confidence + "%"); //Here you have the encode name and the confidence //In my case if the confidence is >50 I return the encode, else I return the default value if (confidence > 50) < charset = cm.getName(); >> 

Не забудьте поставить все, чтобы попытаться поймать его.

Я надеюсь, что это сработает для вас.

Какую библиотеку использовать?

На момент написания этой статьи появляются три библиотеки:

Я не включаю Apache Any23, потому что он использует ICU4j 3.4 под капотом.

Как определить, какая из них обнаружила правильную кодировку (или как можно ближе)?

Невозможно сертифицировать кодировку, обнаруженную каждой из вышеперечисленных библиотек. Тем не менее, можно спросить их по очереди и забрать возвращаемый ответ.

Как получить ответ?

Каждому ответу может быть присвоена одна точка. Чем больше точек ответа, тем больше уверенности в обнаруженной кодировке. Это простой метод подсчета очков. Вы можете уточнить другие.

Есть ли какой-нибудь пример кода?

Вот полный фрагмент, реализующий стратегию, описанную в предыдущих строках.

public static String guessEncoding(InputStream input) throws IOException < // Load input data long count = 0; int n = 0, EOF = -1; byte[] buffer = new byte[4096]; ByteArrayOutputStream output = new ByteArrayOutputStream(); while ((EOF != (n = input.read(buffer))) && (count if (count > Integer.MAX_VALUE) < throw new RuntimeException("Inputstream too large."); >byte[] data = output.toByteArray(); // Detect encoding Map encodingsScores = new HashMap<>(); // * GuessEncoding updateEncodingsScores(encodingsScores, new CharsetToolkit(data).guessEncoding().displayName()); // * ICU4j CharsetDetector charsetDetector = new CharsetDetector(); charsetDetector.setText(data); charsetDetector.enableInputFilter(true); CharsetMatch cm = charsetDetector.detect(); if (cm != null) < updateEncodingsScores(encodingsScores, cm.getName()); >// * juniversalchardset UniversalDetector universalDetector = new UniversalDetector(null); universalDetector.handleData(data, 0, data.length); universalDetector.dataEnd(); String encodingName = universalDetector.getDetectedCharset(); if (encodingName != null) < updateEncodingsScores(encodingsScores, encodingName); >// Find winning encoding Map.Entry maxEntry = null; for (Map.Entry e : encodingsScores.entrySet()) < if (maxEntry == null || (e.getValue()[0] >maxEntry.getValue()[0])) < maxEntry = e; >> String winningEncoding = maxEntry.getKey(); //dumpEncodingsScores(encodingsScores); return winningEncoding; > private static void updateEncodingsScores(Map encodingsScores, String encoding) < String encodingName = encoding.toLowerCase(); int[] encodingScore = encodingsScores.get(encodingName); if (encodingScore == null) < encodingsScores.put(encodingName, new int[] < 1 >); > else < encodingScore[0]++; >> private static void dumpEncodingsScores(Map encodingsScores) < System.out.println(toString(encodingsScores)); >private static String toString(Map encodingsScores) < String GLUE = ", "; StringBuilder sb = new StringBuilder(); for (Map.Entrye : encodingsScores.entrySet()) < sb.append(e.getKey() + ":" + e.getValue()[0] + GLUE); >int len = sb.length(); sb.delete(len - GLUE.length(), len); return "< " + sb.toString() + " >"; > 

Улучшения:
Метод guessEncoding полностью считывает входной поток. Для больших входных потоков это может быть проблемой. Все эти библиотеки будут читать весь входной поток. Это означало бы значительное потребление времени для обнаружения кодировки.

Можно ограничить загрузку начальных данных на несколько байтов и выполнить обнаружение кодировки только для тех немногих байтов.

Насколько я знаю, в этом контексте нет общей библиотеки, которая бы соответствовала бы всем типам проблем. Таким образом, для каждой проблемы вы должны протестировать существующие библиотеки и выбрать лучший, который удовлетворяет вашим ограничениям проблем, но часто ни один из них не подходит. В этих случаях вы можете написать свой собственный детектор кодирования! Как я писал…

Ive написал мета-инструмент Java для обнаружения кодировки кодировки веб-страниц HTML, используя IBM ICU4j и Mozilla JCharDet в качестве встроенных компонентов. Здесь вы можете найти мой инструмент, пожалуйста, прочитайте раздел README перед чем-либо еще. Кроме того, вы можете найти некоторые основные понятия этой проблемы в моей статье и в ее ссылках.

Ниже я представил некоторые полезные комментарии, которые я испытал в своей работе:

  • Обнаружение кодировки не является надежным процессом, потому что оно основано на статистических данных, и на самом деле происходит угадать не обнаружение
  • icu4j является основным инструментом в этом контексте IBM, imho
  • Оба TikaEncodingDetector и Lucene-ICU4j используют icu4j, и их точность не имела существенного отличия от того, что icu4j в моих тестах (самое большее% 1, насколько я помню)
  • icu4j гораздо более общий, чем jchardet, icu4j просто немного предвзято относится к кодировкам семейства IBM, в то время как jchardet сильно привязан к utf-8
  • Из-за широкого использования UTF-8 в HTML-мире; jchardet – лучший выбор, чем icu4j в целом, но это не лучший выбор!
  • icu4j отлично подходит для восточно-азиатских специфических кодировок, таких как EUC-KR, EUC-JP, SHIFT_JIS, BIG5 и кодировки семейства GB
  • Оба icu4j и jchardet являются ошибками при работе с HTML-страницами с кодировками Windows-1251 и Windows-1256. Windows-1251 aka cp1251 широко используется для кириллических языков, таких как русский и Windows-1256, ака cp1256 широко используется для арабского языка.
  • Практически все средства обнаружения кодирования используют статистические методы, поэтому точность вывода сильно зависит от размера и содержимого ввода
  • Некоторые кодировки по существу одинаковы только с частичными отличиями, поэтому в некоторых случаях угаданная или обнаруженная кодировка может быть ложной, но в то же время быть правдой! Что касается Windows-1252 и ISO-8859-1. (см. последний абзац в разделе 5.2 моего доклада).

Альтернативой TikaEncodingDetector является использование Tika AutoDetectReader.

Charset charset = new AutoDetectReader(new FileInputStream(file)).getCharset(); 

Вы можете выбрать соответствующий char набор Конструктор:

new InputStreamReader(new FileInputStream(in), "ISO8859_1"); 

Источник

Читайте также:  Сделать рамку блока css
Оцените статью