ruby 1.9:UTF-8中的无效字节序列

Mar*_*ger 109 ruby encoding utf-8

我在Ruby(1.9)中编写了一个爬虫程序,它从很多随机站点中消耗了大量的HTML.
当试图提取链接时,我决定使用.scan(/href="(.*?)"/i)而不是nokogiri/hpricot(主要加速).问题是我现在收到很多" invalid byte sequence in UTF-8"错误.
根据我的理解,该net/http库没有任何特定于编码的选项,并且所引入的内容基本上没有正确标记.
实际使用传入数据的最佳方法是什么?我尝试.encode使用替换和无效选项集,但到目前为止没有成功...

ece*_*ulm 172

在Ruby 1.9.3中,可以使用String.encode来"忽略"无效的UTF-8序列.这是一个可以在1.8(iconv)和1.9(String #coding)中工作的片段:

require 'iconv' unless String.method_defined?(:encode)
if String.method_defined?(:encode)
  file_contents.encode!('UTF-8', 'UTF-8', :invalid => :replace)
else
  ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
  file_contents = ic.iconv(file_contents)
end
Run Code Online (Sandbox Code Playgroud)

或者如果你有非常麻烦的输入,你可以进行从UTF-8到UTF-16并返回到UTF-8的双重转换:

require 'iconv' unless String.method_defined?(:encode)
if String.method_defined?(:encode)
  file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '')
  file_contents.encode!('UTF-8', 'UTF-16')
else
  ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
  file_contents = ic.iconv(file_contents)
end
Run Code Online (Sandbox Code Playgroud)

  • 还有`force_encoding`选项.如果您读取ISO8859-1作为UTF-8(因此该字符串包含无效的UTF-8),那么您可以使用the_string.force_encoding("ISO8859-1")将其"重新解释"为ISO8859-1并且正常工作使用该字符串进行实际编码. (7认同)
  • 我认为双重转换是有效的,因为它强制进行编码转换(并检查无效字符).如果源字符串已经以UTF-8编码,那么只调用`.encode('UTF-8')`是无操作,并且不运行任何检查.[用于编码的Ruby核心文档](http://www.ruby-doc.org/core-1.9.3/String.html#method-i-encode).但是,将其转换为UTF-16会首先强制执行对无效字节序列的所有检查,并根据需要进行替换. (5认同)
  • 对于一些有问题的输入,我还使用从UTF-8到UTF-16的双重转换,然后返回到UTF-8`file_contents.encode!('UTF-16','UTF-8',:invalid =>:replace, :replace =>'')``file_contents.encode!('UTF-8','UTF-16')` (3认同)
  • 这个双重编码技巧刚刚保存了我的培根!我想知道为什么它是必需的? (3认同)

Ami*_*far 79

接受的答案和其他答案对我有用.我发现这个职位这表明

string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
Run Code Online (Sandbox Code Playgroud)

这解决了我的问题.


Mar*_*ger 23

我目前的解决方案是运行:

my_string.unpack("C*").pack("U*")
Run Code Online (Sandbox Code Playgroud)

这将至少摆脱我的主要问题的例外

  • 我正在使用这个方法与`valid_encoding?`结合使用,它似乎可以检测到什么时候出错了.`val.unpack('C*').pack('U*')if!val.valid_encoding?`. (3认同)

Ran*_*avi 8

试试这个:

def to_utf8(str)
  str = str.force_encoding('UTF-8')
  return str if str.valid_encoding?
  str.encode("UTF-8", 'binary', invalid: :replace, undef: :replace, replace: '')
end
Run Code Online (Sandbox Code Playgroud)


Adr*_*ian 0

在使用 之前scan,请确保请求的页面的Content-Type标头是text/html,因为可能存在指向未以 UTF-8 编码的图像等内容的链接。href如果您在类似元素之类的内容中选择了 a,则该页面也可能是非 html <link>。如何检查这一点取决于您使用的 HTTP 库。然后,确保结果仅是 ascii String#ascii_only?(不是 UTF-8,因为 HTML 应该只使用 ascii,否则可以使用实体)。如果这两项测试均通过,则可以安全使用scan