Гадание при строковых кодировках для потока байтов в файле журнала - PullRequest
4 голосов
/ 13 февраля 2012

tl; dr summary : Учитывая поток байтов, представляющих строку в неизвестной кодировке, какие кодировки и в каком порядке я должен пытаться интерпретировать байты, чтобы получить наилучший шанс найти «правильное» кодировка?

Пример задачи

У меня есть файл arrows.txt, который, как я знаю, был сохранен с использованием UTF-8 с односимвольным содержимым . Если я притворяюсь, что я не знаю , какова кодировка этого файла, произойдет сбой следующего кода Ruby в Windows:

s = IO.read('foo.txt')
p s.encoding,        #=> #<Encoding:IBM437>
  s.valid_encoding?, #=> true
  s.chars.to_a       #=> ["\xE2", "\x87", "\x88"]

Он «не работает», потому что сообщает, что файл действительно содержал Γçê и что все в порядке (кодировка действительна).

Сценарий реального мира

У меня есть Nginx файлы журнала и файлы журнала Akamai, которые не имеют какой-либо конкретной кодировки для запросов, которые они записывают, которые мне нужно обрабатывать и хранить данные в базе данных как UTF-8. Большую часть времени интерпретация каждой строки как UTF-8 приводит к строке с правильной кодировкой, но иногда это не так.

Я хочу попросить Ruby попробовать различные кодировки для каждой строки, найти правильную и вероятную (но, конечно, не гарантированную) для правильной.

Неудачная попытка

Первоначально я написал следующий код:

def guess_encoding( str, result='utf-8', *encodings )
  # Try every encoding if none were passed in
  encodings = Encoding.list if encodings.empty?

  # Keep forcing a new encoding until we find one that is valid
  unless encodings.find{ |e| str.force_encoding(e) && str.valid_encoding? }
    raise "None of the supplied encodings was valid"
  end

  # Convert from the valid encoding to the desired, replacing 'bad' characters
  str.encode(result, invalid: :replace, undef: :replace)
end

Проблема в том, что самой первой кодировкой в ​​Encoding.list является ASCII-8BIT, которая действительна для всех потоков байтов. Таким образом, если я использую свой код выше и вызываю s2 = guess_encoding(s), результатом будет строка ��� для моего трехбайтового символа двойной стрелки выше.

Наконец, вопрос (ы)

В каком порядке я должен проверять кодировки, чтобы обеспечить наибольший шанс, что первый valid_encoding? будет правильным? Какие общие кодировки являются самыми требовательными к используемым байтам, так что я должен их сначала попробовать, и какие общие кодировки являются полностью допустимыми, так что я должен попробовать их последними?

Есть ли другие эвристики, которые я должен использовать, чтобы угадать правильность? (С большей вероятностью будет правильным, если определенная кодировка приводит к меньшему количеству символов, чем другая?)

Ответы [ 3 ]

2 голосов
/ 13 февраля 2012

Вы можете попробовать камень rchardet19 .Он «берет последовательность байтов в неизвестной кодировке символов и пытается определить кодировку».Это также дает вам показатель достоверности для кодировки, которую он возвращает.Он работал для меня несколько раз в прошлом, и похоже, что он делает то, что вы пытаетесь достичь.

Пример использования:

require 'rchardet19'
cd = CharDet.detect("some data")
# => #<struct #<Class:0x102216198> encoding="ascii", confidence=1.0>
1 голос
/ 13 февраля 2012

Когда я имел дело с написанием пауков, я всегда начинал с ISO-8859-1, а затем с Win-1252. Разница между ними незначительна, поэтому большинство из них должно подходить в большинстве случаев. Во-первых, я считаю, что вы, скорее всего, столкнетесь с ними.

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

Были времена, когда ничто не было бы ударом; У меня был код, который извлек все кодировки iconv, а затем удалил все значения ASCII и попытался найти кодировку с наибольшим количеством совпадений для оставшихся символов. XML и HTML иногда были настолько уродливы, что ничего не помогало, и тогда я вернулся к удалению акцентов.

1 голос
/ 13 февраля 2012

Если ваш код может работать на машине с Unix / Linux, то гем filemagic может вам помочь.

gem install ruby-filemagic

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

$ irb 
irb(main):001:0> require 'filemagic' 
=> true
irb(main):002:0> fm = FileMagic.new
=> #<FileMagic:0x7fd4afb0>
irb(main):003:0> fm.file('afile.zip') 
=> "Zip archive data, at least v2.0 to extract"
irb(main):004:0>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...