Как я могу идентифицировать различные кодировки без использования спецификации? - PullRequest
0 голосов
/ 28 августа 2009

У меня есть средство просмотра файлов, которое получает содержимое из растущего файла, кодированного с помощью utf-16LE. У первого бита данных, записанных в него, есть доступная спецификация - я использовал это для идентификации кодировки по UTF-8 (в которую МОЖЕТ большинство моих входящих файлов, закодированы). Я ловлю BOM и перекодирую в UTF-8, чтобы мой парсер не волновался. Проблема в том, что, поскольку это растущий файл, не каждый бит данных содержит спецификацию.

Вот мой вопрос - не добавляя байты спецификации к каждому набору данных (, поскольку у меня нет контроля над источником ), я могу просто искать нулевые байты, которые присущи UTF-16 \ 000, а затем использовать это как мой идентификатор вместо спецификации? Это вызовет у меня головную боль в будущем?

Моя архитектура включает в себя веб-приложение ruby, регистрирующее полученные данные во временном файле, когда мой анализатор, написанный на java, забирает их.

Напишите сейчас мой код идентификации / перекодировки выглядит следующим образом:

  // guess encoding if utf-16 then
  // convert to UTF-8 first
  try {
    FileInputStream fis = new FileInputStream(args[args.length-1]);
    byte[] contents = new byte[fis.available()];
    fis.read(contents, 0, contents.length);

    if ( (contents[0] == (byte)0xFF) && (contents[1] == (byte)0xFE) ) {
      String asString = new String(contents, "UTF-16");
      byte[] newBytes = asString.getBytes("UTF8");
      FileOutputStream fos = new FileOutputStream(args[args.length-1]);
      fos.write(newBytes);
      fos.close();
    }

    fis.close();
    } catch(Exception e) {
      e.printStackTrace();
  }

UPDATE

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

  // guess encoding if utf-16 then
  // convert to UTF-8 first
  try {
    FileInputStream fis = new FileInputStream(args[args.length-1]);
    byte[] contents = new byte[fis.available()];
    fis.read(contents, 0, contents.length);
    byte[] real = null;

    int found = 0;

    // if found a BOM then skip out of here... we just need to convert it
    if ( (contents[0] == (byte)0xFF) && (contents[1] == (byte)0xFE) ) {
      found = 3;
      real = contents;

    // no BOM detected but still could be UTF-16
    } else {

      for(int cnt=0; cnt<10; cnt++) {
        if(contents[cnt] == (byte)0x00) { found++; };

        real = new byte[contents.length+2];
        real[0] = (byte)0xFF;
        real[1] = (byte)0xFE;

        // tack on BOM and copy over new array
        for(int ib=2; ib < real.length; ib++) {
          real[ib] = contents[ib-2];
        }
      }

    }

    if(found >= 2) {
      String asString = new String(real, "UTF-16");
      byte[] newBytes = asString.getBytes("UTF8");
      FileOutputStream fos = new FileOutputStream(args[args.length-1]);
      fos.write(newBytes);
      fos.close();
    }

    fis.close();
    } catch(Exception e) {
      e.printStackTrace();
  }

Что вы все думаете?

Ответы [ 3 ]

6 голосов
/ 28 августа 2009

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

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

РЕДАКТИРОВАТЬ: из ваших комментариев к вопросу, файлы данных доставляются через HTTP. В этом случае вы должны организовать, чтобы ваш HTTP-сервер воспринимал заголовок «content-type» POST-запросов, доставляющих данные, извлекал набор символов / кодировку из заголовка и сохранял его так, чтобы ваш анализатор файлов мог иметь дело.

0 голосов
/ 28 августа 2009

Этот вопрос содержит несколько опций для обнаружения символов, которые, по-видимому, не требуют спецификации.

Мой проект в настоящее время использует jCharDet , но мне может понадобиться взглянуть на некоторые другие опции, перечисленные там, поскольку jCharDet не на 100% надежен.

0 голосов
/ 28 августа 2009

Это вызовет у вас головную боль в будущем, без сомнения. Вы можете проверить наличие чередующихся нулевых байтов для упрощенных случаев (только ASCII, UTF-16, любой порядок байтов), но в ту минуту, когда вы начинаете получать поток символов выше кодовой точки 0x7f, этот метод становится бесполезным.

Если у вас есть дескриптор файла, лучше всего сохранить текущий указатель файла, перейти к началу, прочитать спецификацию, а затем вернуться к исходной позиции.

Либо так, либо как-нибудь запомните спецификацию.

Полагаться на содержимое данных - это плохая идея, если вы не абсолютно уверены, что диапазон символов будет ограничен для всех входных данных.

...