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

时间:2010-06-06 00:35:35

标签: ruby encoding utf-8

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

11 个答案:

答案 0 :(得分:170)

在Ruby 1.9.3中,可以使用String.encode来“忽略”无效的UTF-8序列。这是一个可在1.8(iconv)和1.9(String#encode)中使用的代码段:

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

答案 1 :(得分:78)

接受的答案或其他答案对我有用。我找到了建议的this post

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

这解决了我的问题。

答案 2 :(得分:23)

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

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

这至少可以摆脱我的主要问题

答案 3 :(得分: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

答案 4 :(得分:4)

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

解析HTML并不像看起来那么容易。

浏览器在UTF-8 HTML文档中解析无效的UTF-8序列,只需添加“ ”符号。因此,一旦HTML中的无效UTF-8序列被解析,结果文本就是一个有效的字符串。

即使在属性值内,您也必须解码HTML实体,如放大器

这是一个很好的问题,总结了为什么你不能用正则表达式可靠地解析HTML: RegEx match open tags except XHTML self-contained tags

答案 5 :(得分:3)

这似乎有效:

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

答案 6 :(得分:3)

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

答案 7 :(得分:2)

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

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

答案 8 :(得分:1)

虽然Nakilon的解决方案有效,至少就错过了错误,在我的情况下,我有一个奇怪的f-ed up字符源自Microsoft Excel转换为CSV,在ruby中注册为(得到这个)西里尔字母在红宝石中的K是一个粗体K.为了解决这个问题,我用'iso-8859-1'即可。 CSV.parse(f, :encoding => "iso-8859-1"),这使我的怪异的dehy西里尔文K变成了一个更易于管理的/\xCA/,然后我可以使用string.gsub!(/\xCA/, '')删除

答案 9 :(得分:0)

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

答案 10 :(得分:-1)

如果您不“关心”数据,您可以执行以下操作:

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

我刚用valid_encoding?来传递它。我是一个搜索领域,所以我一遍又一遍地发现同样的怪异所以我使用了类似的东西:只是为了让系统不破坏。由于我在发送此信息之前不会将用户体验控制为autovalidate(如自动反馈说“虚拟!”),我可以将其取出,将其删除并返回空白结果。