ruby 1.9: invalid byte sequence in UTF-8

我正在用 Ruby (1.9)编写一个爬虫程序,它从很多随机的站点中消耗大量 HTML。
在尝试提取链接时,我决定只使用 .scan(/href="(.*?)"/i)而不是 nokogiri/hpicot (主要加速)。问题是,我现在收到很多“ invalid byte sequence in UTF-8”错误。
据我所知,net/http库没有任何特定的编码选项,输入的内容基本上没有正确的标记。
What would be the best way to actually work with that incoming data? I tried .encode with the replace and invalid options set, but no success so far...

137542 次浏览

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

我建议您使用 HTML 解析器。

Parsing HTML is not as easy as it may seem.

浏览器解析无效的 UTF-8序列,在 UTF-8 HTML 文档中,只需输入“”符号。因此,一旦解析了 HTML 中无效的 UTF-8序列,生成的文本就是一个有效的字符串。

Even inside attribute values you have to decode HTML entities like amp

下面是一个很好的问题,它总结了为什么不能用正则表达式可靠地解析 HTML: 除了 XHTML 自包含的标记之外,RegEx 与打开的标记匹配

我遇到过字符串,它混合了英语、俄语和其他一些字母,这导致了异常。我只需要俄语和英语,这目前为我工作:

ec1 = Encoding::Converter.new "UTF-8","Windows-1251",:invalid=>:replace,:undef=>:replace,:replace=>""
ec2 = Encoding::Converter.new "Windows-1251","UTF-8",:invalid=>:replace,:undef=>:replace,:replace=>""
t = ec2.convert ec1.convert t

我目前的解决办法是逃跑:

my_string.unpack("C*").pack("U*")

这至少可以消除我的主要问题——例外情况

在 Ruby 1.9.3中,可以使用 String.encode“忽略”无效的 UTF-8序列。下面是一个同时适用于1.8(iconv)和1.9(字符串 # 编码)的代码片段:

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

或者,如果输入非常麻烦,可以进行从 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

虽然 Nakilon 的解决方案起作用了,至少在克服这个错误方面是这样的,在我的例子中,我有一个从 Microsoft Excel 转换成 CSV 的奇怪的字符,它在 ruby 中注册为一个西里尔字母 K,在 ruby 中是一个粗体字母 K。为了解决这个问题,我使用了‘ iso-8859-1’viz.CSV.parse(f, :encoding => "iso-8859-1"),它把我奇怪的西里尔字母 K 变成了一个更容易管理的 /\xCA/,然后我可以用 string.gsub!(/\xCA/, '')删除它

这似乎行得通:

def sanitize_utf8(string)
return nil if string.nil?
return string if string.valid_encoding?
string.chars.select { |c| c.valid_encoding? }.join
end

接受的答案或其他的答案为我工作。我找到了 这篇文章的建议

string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')

这为我解决了问题。

如果你不“关心”这些数据,你可以这样做:

search_params = params[:search].valid_encoding? ? params[:search].gsub(/\W+/, '') : "nothing"

I just used valid_encoding? to get passed it. Mine is a search field, and so i was finding the same weirdness over and over so I used something like: just to have the system not break. Since i don't control the user experience to autovalidate prior to sending this info (like auto feedback to say "dummy up!") I can just take it in, strip it out and return blank results.

试试这个:

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
attachment = file.read


begin
# Try it as UTF-8 directly
cleaned = attachment.dup.force_encoding('UTF-8')
unless cleaned.valid_encoding?
# Some of it might be old Windows code page
cleaned = attachment.encode( 'UTF-8', 'Windows-1252' )
end
attachment = cleaned
rescue EncodingError
# Force it to UTF-8, throwing out invalid bits
attachment = attachment.force_encoding("ISO-8859-1").encode("utf-8", replace: nil)
end

还有用于筛选无效字节的 方法。

string.scrub('')