Как я могу идентифицировать различные кодировки для файлов без использования спецификации и начиная с не-ASCII символа? - PullRequest
5 голосов
/ 14 апреля 2011

У меня возникла проблема при попытке определить кодировку файла без спецификации, особенно когда файл начинается с символов, отличных от ascii.

Я нашел следующие две темы о том, как идентифицировать кодировки для файлов,

В настоящее время я создал класс для идентификации различных кодировок для файлов (например, UTF-8, UTF-16, UTF-32, UTF-16 без спецификации и т. Д.), Как показано ниже,

public class UnicodeReader extends Reader {
private static final int BOM_SIZE = 4;
private final InputStreamReader reader;

/**
 * Construct UnicodeReader
 * @param in Input stream.
 * @param defaultEncoding Default encoding to be used if BOM is not found,
 * or <code>null</code> to use system default encoding.
 * @throws IOException If an I/O error occurs.
 */
public UnicodeReader(InputStream in, String defaultEncoding) throws IOException {
    byte bom[] = new byte[BOM_SIZE];
    String encoding;
    int unread;
    PushbackInputStream pushbackStream = new PushbackInputStream(in, BOM_SIZE);
    int n = pushbackStream.read(bom, 0, bom.length);

    // Read ahead four bytes and check for BOM marks.
    if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) {
        encoding = "UTF-8";
        unread = n - 3;
    } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
        encoding = "UTF-16BE";
        unread = n - 2;
    } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
        encoding = "UTF-16LE";
        unread = n - 2;
    } else if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
        encoding = "UTF-32BE";
        unread = n - 4;
    } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
        encoding = "UTF-32LE";
        unread = n - 4;
    } else {
        // No BOM detected but still could be UTF-16
        int found = 0;
        for (int i = 0; i < 4; i++) {
            if (bom[i] == (byte) 0x00)
                found++;
        }

        if(found >= 2) {
            if(bom[0] == (byte) 0x00){
                encoding = "UTF-16BE";
            }
            else {
                encoding = "UTF-16LE";
            }
            unread = n;
        }
        else {
            encoding = defaultEncoding;
            unread = n;
        }
    }

    // Unread bytes if necessary and skip BOM marks.
    if (unread > 0) {
        pushbackStream.unread(bom, (n - unread), unread);
    } else if (unread < -1) {
        pushbackStream.unread(bom, 0, 0);
    }

    // Use given encoding.
    if (encoding == null) {
        reader = new InputStreamReader(pushbackStream);
    } else {
        reader = new InputStreamReader(pushbackStream, encoding);
    }
}

public String getEncoding() {
    return reader.getEncoding();
}

public int read(char[] cbuf, int off, int len) throws IOException {
    return reader.read(cbuf, off, len);
}

public void close() throws IOException {
    reader.close();
}
* *} Тысяча двадцать-один

Приведенный выше код может работать правильно во всех случаях, кроме случаев, когда файл без спецификации и начинается с символов, отличных от ascii. Так как при этом обстоятельстве логика проверки того, что файл все еще будет UTF-16 без спецификации, не будет работать правильно, и кодировка будет установлена ​​как UTF-8 по умолчанию.

Если есть способ проверить кодировки файла без спецификации и начинать с символов, отличных от ascii, особенно для файла UTF-16 NO BOM?

Спасибо, любая идея будет оценена.

Ответы [ 3 ]

1 голос
/ 14 апреля 2011

Лучший подход - не пытаться реализовать это самостоятельно. Вместо этого используйте существующую библиотеку, чтобы сделать это; см. Java: как определить правильную кодировку кодировки потока . Например:

Следует отметить, что лучшее, что можно сделать, - это угадать наиболее вероятную кодировку для файла. В общем случае невозможно быть на 100% уверенным, что вы выяснили правильную кодировку; то есть кодировка, которая использовалась при создании файла.


Я бы сказал, что эти сторонние библиотеки также не могут идентифицировать кодировки для файла, с которым я столкнулся [...], они могут быть улучшены для удовлетворения моих требований.

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

  • ограничить себя определенным набором кодировок,
  • настаивают на том, чтобы лицо, предоставляющее / загружающее файл, правильно указывало его кодировку (или основной язык) и / или
  • признайте, что ваша система будет ошибаться в определенный процент времени, и предоставьте средства, с помощью которых кто-то может исправить неправильно заявленные / угаданные кодировки.

Признайте факты: это теоретически неразрешимая проблема.

1 голос
/ 14 апреля 2011

Вообще говоря, невозможно точно узнать кодировку, если она не указана.

Вы можете угадать UTF-8 по определенному шаблону в текстах (старший бит установлен, установлен, установлен, не установлен, установлен, установлен, установлен, не установлен), но это все еще предположение.

UTF-16 сложный; вы можете успешно проанализировать BE и LE в одном потоке; в обоих случаях он выдаст несколько символов (хотя и потенциально бессмысленный текст).

В некотором коде используется статистический анализ, позволяющий угадать кодировку по частоте символов, но для этого требуются некоторые предположения относительно текста (т. Е. «Это монгольский текст») и таблиц частот (которые могут не соответствовать тексту) , В конце концов, это всего лишь предположение, и оно не может помочь в 100% случаев.

0 голосов
/ 14 апреля 2011

Если вы уверены, что это действительный поток Unicode, это должно быть UTF-8, если у него нет спецификации (поскольку спецификация не требуется и не рекомендуется), и если она есть, то вы знаете, что это такое..

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

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

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

У меня такое ощущение, что вы, должно быть, человек из Windows, поскольку у остальных из нас, в первую очередь, есть причины для спецификаций.Я знаю, что я регулярно имею дело с тгагабайтами текста (на компьютерах Mac, Linux, Solaris и BSD), более 99% из которых UTF-8, и только дважды я сталкивался с текстом, загруженным спецификациями.Я слышал, что люди из Windows все время застревают.Если это правда, это может или не может сделать ваш выбор легче.

...