Со ссылкой на следующий поток: Приложение Java: невозможно правильно прочитать файл в кодировке iso-8859-1
Каков наилучший способ программного определения правильной кодировки кодировки входного потока / файла?
Я пробовал использовать следующее:
File in = new File(args[0]);
InputStreamReader r = new InputStreamReader(new FileInputStream(in));
System.out.println(r.getEncoding());
Но для файла, который, как я знаю, закодирован с помощью ISO8859_1, приведенный выше код дает ASCII, что неверно и не позволяет мне правильно отобразить содержимое файла обратно на консоль.
Reader.getEncoding
возвращает кодировку, для использования которой был настроен читатель, которая в вашем случае является кодировкой по умолчанию.Ответы:
Я использовал эту библиотеку, похожую на jchardet, для определения кодировки в Java: http://code.google.com/p/juniversalchardet/
источник
Вы не можете определить кодировку произвольного байтового потока. Такова природа кодировок. Кодирование означает отображение между байтовым значением и его представлением. Так что каждая кодировка «могла быть» правильной.
Метод getEncoding () вернет кодировку, которая была настроена (прочтите JavaDoc ) для потока. Он не угадает за вас кодировку.
Некоторые потоки сообщают вам, какая кодировка использовалась для их создания: XML, HTML. Но не произвольный поток байтов.
В любом случае, вы можете попытаться угадать кодировку самостоятельно, если вам нужно. Каждый язык имеет общую частоту для каждого символа. В английском языке символ 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 { throw new UnsupportedCharsetException() }
источник
Вот мои любимые:
TikaEncodingDetector
Зависимость:
<dependency> <groupId>org.apache.any23</groupId> <artifactId>apache-any23-encoding</artifactId> <version>1.1</version> </dependency>
Образец:
public static Charset guessCharset(InputStream is) throws IOException { return Charset.forName(new TikaEncodingDetector().guessEncoding(is)); }
GuessEncoding
Зависимость:
<dependency> <groupId>org.codehaus.guessencoding</groupId> <artifactId>guessencoding</artifactId> <version>1.4</version> <type>jar</type> </dependency>
Образец:
public static Charset guessCharset2(File file) throws IOException { return CharsetToolkit.guessEncoding(file, 4096, StandardCharsets.UTF_8); }
источник
CharsetDectector
класса ICU4J 3.4 .Вы, безусловно, можете проверить файл для конкретной кодировки, декодируя его с помощью a
CharsetDecoder
и отслеживая ошибки "неправильного ввода" или "несопоставимого символа". Конечно, это только говорит вам, если кодировка неверна; он не говорит вам, правильно ли это. Для этого вам нужна основа сравнения для оценки декодированных результатов, например, знаете ли вы заранее, ограничены ли символы некоторым подмножеством или придерживается ли текст какого-то строгого формата? Суть в том, что определение кодировки - это догадки без каких-либо гарантий.источник
Какую библиотеку использовать?
На момент написания этой статьи появилось три библиотеки:
Я не включаю 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 <= Integer.MAX_VALUE)) { output.write(buffer, 0, n); count += n; } if (count > Integer.MAX_VALUE) { throw new RuntimeException("Inputstream too large."); } byte[] data = output.toByteArray(); // Detect encoding Map<String, int[]> 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<String, int[]> maxEntry = null; for (Map.Entry<String, int[]> 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<String, int[]> 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<String, int[]> encodingsScores) { System.out.println(toString(encodingsScores)); } private static String toString(Map<String, int[]> encodingsScores) { String GLUE = ", "; StringBuilder sb = new StringBuilder(); for (Map.Entry<String, int[]> e : encodingsScores.entrySet()) { sb.append(e.getKey() + ":" + e.getValue()[0] + GLUE); } int len = sb.length(); sb.delete(len - GLUE.length(), len); return "{ " + sb.toString() + " }"; }
Улучшения:
guessEncoding
метод считывает InputStream полностью. Для больших входных потоков это может быть проблемой. Все эти библиотеки будут читать весь поток ввода. Это потребовало бы больших затрат времени на определение кодировки.Можно ограничить начальную загрузку данных несколькими байтами и выполнить определение кодировки только на этих нескольких байтах.
источник
Приведенные выше библиотеки представляют собой простые детекторы спецификации, которые, конечно, работают только в том случае, если в начале файла есть спецификация. Взгляните на http://jchardet.sourceforge.net/, который сканирует текст
источник
Насколько мне известно, в этом контексте нет общей библиотеки, подходящей для всех типов задач. Итак, для каждой проблемы вы должны протестировать существующие библиотеки и выбрать лучшую, которая удовлетворяет ограничениям вашей проблемы, но часто ни одна из них не подходит. В этих случаях вы можете написать свой собственный детектор кодирования! Как я уже писал ...
Я написал мета-инструмент Java для определения кодировки кодировки веб-страниц HTML, используя IBM ICU4j и Mozilla JCharDet в качестве встроенных компонентов. Здесь вы можете найти мой инструмент, прежде всего прочтите раздел README. Кроме того, вы можете найти некоторые основные концепции этой проблемы в моей статье и в ссылках на нее.
Ниже я предоставил несколько полезных комментариев, которые я испытал в своей работе:
источник
Я нашел хорошую стороннюю библиотеку, которая может определять фактическую кодировку: http://glaforge.free.fr/wiki/index.php?wiki=GuessEncoding
Я не тестировал его тщательно, но, похоже, он работает.
источник
Если вы используете ICU4J ( http://icu-project.org/apiref/icu4j/ )
Вот мой код:
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(); } }
Не забудьте поставить все, что нужно попробовать-поймать.
Надеюсь, это сработает для вас.
источник
Если вы не знаете кодировку своих данных, ее не так просто определить, но вы можете попробовать использовать библиотеку, чтобы угадать это . Также есть похожий вопрос .
источник
Для файлов ISO8859_1 не существует простого способа отличить их от ASCII. Однако для файлов Unicode это обычно можно обнаружить на основе первых нескольких байтов файла.
Файлы UTF-8 и UTF-16 включают метку порядка байтов (BOM) в самом начале файла. Спецификация представляет собой неразрывное пространство нулевой ширины.
К сожалению, по историческим причинам Java не обнаруживает это автоматически. Такие программы, как Блокнот, проверяют спецификацию и используют соответствующую кодировку. Используя unix или Cygwin, вы можете проверить спецификацию с помощью команды file. Например:
$ file sample2.sql sample2.sql: Unicode text, UTF-16, big-endian
Для Java я предлагаю вам проверить этот код, который определит распространенные форматы файлов и выберет правильную кодировку: Как читать файл и автоматически указывать правильную кодировку
источник
Альтернативой TikaEncodingDetector является использование Tika AutoDetectReader .
Charset charset = new AutoDetectReader(new FileInputStream(file)).getCharset();
источник
На простой Java:
final String[] encodings = { "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16BE", "UTF-16LE", "UTF-16" }; List<String> lines; for (String encoding : encodings) { try { lines = Files.readAllLines(path, Charset.forName(encoding)); for (String line : lines) { // do something... } break; } catch (IOException ioe) { System.out.println(encoding + " failed, trying next."); } }
Этот подход будет пробовать кодировки одну за другой, пока одна из них не сработает или они не закончатся. (Кстати, в моем списке кодировок есть только эти элементы, потому что они являются реализациями кодировок, необходимыми на каждой платформе Java, https://docs.oracle.com/javase/9/docs/api/java/nio/charset/Charset.html )
источник
Можете ли вы выбрать соответствующий набор символов в конструкторе :
new InputStreamReader(new FileInputStream(in), "ISO8859_1");
источник