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

ruby 1.9:UTF-8中的字节序列无效,ruby,encoding,utf-8,Ruby,Encoding,Utf 8,我正在用Ruby(1.9)编写一个爬虫程序,它从许多随机站点中消耗大量HTML。 在尝试提取链接时,我决定只使用.scan(/href=“(.*?)”/I)而不是nokogiri/hpricot(主要加速)。问题是,我现在收到了大量UTF-8中的“无效字节序列”错误。 据我所知,net/http库没有任何特定于编码的选项,而且输入的内容基本上没有正确标记。 实际处理传入数据的最佳方式是什么?我尝试了.encode,设置了替换和无效选项,但到目前为止没有成功…在使用扫描之前,请确保请求页面的内容类

我正在用Ruby(1.9)编写一个爬虫程序,它从许多随机站点中消耗大量HTML。
在尝试提取链接时,我决定只使用
.scan(/href=“(.*?)”/I)
而不是nokogiri/hpricot(主要加速)。问题是,我现在收到了大量UTF-8中的“
无效字节序列”错误。
据我所知,
net/http
库没有任何特定于编码的选项,而且输入的内容基本上没有正确标记。

实际处理传入数据的最佳方式是什么?我尝试了
.encode
,设置了替换和无效选项,但到目前为止没有成功…

在使用
扫描
之前,请确保请求页面的
内容类型
标题为
文本/html
,因为可能有指向未在UTF-8中编码的图像等内容的链接。如果在
元素中选择
href
,则该页面也可能是非html的。如何检查这一点取决于您使用的HTTP库。然后,确保结果仅为带有
字符串的ascii#ascii_only?
(不是UTF-8,因为HTML应该只使用ascii,否则可以使用实体)。如果这两个测试都通过,那么可以安全地使用
scan

我建议您使用HTML解析器。只要找到最快的

解析HTML并不像看上去那么容易

浏览器在UTF-8 HTML文档中解析无效的UTF-8序列,只需将�" 所以一旦HTML中无效的UTF-8序列被解析,结果文本就是一个有效的字符串

即使在属性值内部,您也必须对amp等HTML实体进行解码

下面是一个很好的问题,总结了为什么不能用正则表达式可靠地解析HTML:

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

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()和1.9()中都可以使用的代码片段:

或者,如果您的输入非常麻烦,您可以进行从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的奇怪的f-ed up字符转换为CSV,在ruby中注册为(获取这个)西里尔字母K,在ruby中是一个粗体的K。为了解决这个问题,我使用了“iso-8859-1”,即
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\u params=params[:search]。有效的\u编码??params[:search]。gsub(/\W+/,“”):“无”

我只是使用了
有效编码?
来通过它。我的是一个搜索字段,所以我一遍又一遍地发现了同样的奇怪之处,所以我使用了类似的东西:只是为了让系统不崩溃。因为我不控制用户体验在发送此信息之前自动验证(就像自动反馈说“dummy up!”)我可以把它取进去,去掉,然后返回空白结果。

试试这个:

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

谢谢,但这不是我的问题:)我只提取了URL的主机部分,只点击了首页。我的问题是,我的输入显然不是UTF-8,1.9编码的foo没有haywire@Marc西格尔:“我的意见”是什么意思"? Stdin、URL或页面主体?HTML可以用UTF-8编码:我的输入=页面主体@Eduardo:我知道。我的问题是,来自net/http的数据似乎不时有一个错误的编码。对于网页来说,真正的错误编码并不少见。响应头可能会说这是一种编码,但实际上提供了另一种编码。我希望保留regexp,因为它大约快10倍,我真的不想正确解析html,只想提取链接。我应该能够通过执行以下操作来替换ruby中的无效部分:ok_string=bad_string.encode(“UTF-8”,{:invalid=>:replace,:undef=>:replace}),但这似乎不起作用:(可能会破坏字符,但使字符串对其他库有效:valid_string=untrusted_string.unpack('C*').pack('U*'))有了确切的问题,尝试了相同的其他解决方案。没有爱情。尝试了马克的,但似乎一切都乱七八糟。你确定
'U*'
撤销
'C*'
?不,它没有:)我只是在一个webcrawler中使用了它,在这里我关心的是第三方库不会崩溃,而不是这里或那里的一句话。在一些有问题的输入中,我还使用了从UTF-8到UTF-16的双重转换,然后再回到UTF-8
file_contents.encode!('UTF-16','UTF-8',:无效=>:replace,:replace=>'')
文件\u contents.encode!('UTF-8'、'UTF-16')
还有
force_encoding
选项。如果您将ISO8859-1读取为UTF-8(因此该字符串包含无效的UTF-8),那么您可以使用_字符串“重新解释”为ISO8859-1。强制_编码(“ISO8859-1”),并以实际编码方式处理该字符串。这种双重编码技巧刚刚救了我的命!我想知道为什么需要它?我应该把这些行放在哪里?我认为双重转换是有效的,因为它强制进行编码转换(并检查无效字符)。如果源字符串已经用UTF-8编码,那么只调用
.encode('UTF-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
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