tl;dr summary:给定一个表示未知编码字符串的字节流,我应该尝试用什么编码以及以什么顺序解释这些字节以获得找到 '正确的'编码?
问题示例
我有一个文件 arrows.txt
,我碰巧知道它是使用 UTF-8 保存的,单字符内容为 ⇈
。如果我假装我不知道这个文件的编码是什么,Windows 上的以下 Ruby 代码将失败:
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?
成为正确的编码?哪些常见编码对使用的字节最挑剔,因此我应该首先尝试它们,哪些常见编码是完全宽松的,因此我应该最后尝试它们?
在猜测正确性时,我应该使用任何其他启发式方法吗? (如果特定编码产生的字符数少于另一种编码,是否更有可能是正确的?)
最佳答案
你可以试试 rchardet19 gem 。它“采用未知字符编码的字节序列,并尝试确定编码。”它还为您返回的编码提供置信度分数。它在过去曾多次为我工作,看起来它完成了您想要完成的事情。
示例用法:
require 'rchardet19'
cd = CharDet.detect("some data")
# => #<struct #<Class:0x102216198> encoding="ascii", confidence=1.0>
关于ruby - 猜测日志文件中字节流的字符串编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9265605/