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?
будет правильным? Какие общие кодировки являются самыми требовательными к используемым байтам, так что я должен их сначала попробовать, и какие общие кодировки являются полностью допустимыми, так что я должен попробовать их последними?
Есть ли другие эвристики, которые я должен использовать, чтобы угадать правильность? (С большей вероятностью будет правильным, если определенная кодировка приводит к меньшему количеству символов, чем другая?)