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

Posted

技术标签:

【中文标题】ruby 1.9:UTF-8 中的无效字节序列【英文标题】:ruby 1.9: invalid byte sequence in UTF-8 【发布时间】:2011-02-28 06:54:21 【问题描述】:

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

【问题讨论】:

可能会破坏字符,但保持字符串对其他库有效:valid_string = untrusted_string.unpack('C*').pack('U*') 遇到确切问题,尝试了相同的其他解决方案。没爱。试过马克的,但它似乎乱码。你确定'U*' 撤消'C*' 不,它没有 :) 我只是在一个网络爬虫中使用了它,我关心 3rd 方库不会崩溃,而不是我在这里和那里的一句话。 【参考方案1】:

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

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

这解决了我的问题。

【讨论】:

这解决了我的问题,我喜欢使用非弃用的方法(我现在有 Ruby 2.0)。 这个是唯一有效的!我已经尝试了上述所有解决方案,但没有一个可以工作 String that used in testing "fdsfdsf dfsf sfds fs sdf hello

fooo??? !@#$%^&*()_+

\xEF\xBF\xBD \xef\xbf\x9c \xc2\x90 \xc2\x90"
第二个参数 'binary' 是干什么用的?【参考方案2】:
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

【讨论】:

【参考方案3】:

试试这个:

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】:

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

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

【讨论】:

由于输入有问题,我还使用了从 UTF-8 到 UTF-16 的双重转换,然后再返回到 UTF-8 file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '') file_contents.encode!('UTF-8', 'UTF-16') 还有force_encoding的选项。如果您将 ISO8859-1 读取为 UTF-8(因此该字符串包含无效的 UTF-8),那么您可以使用 the_string.force_encoding("ISO8859-1") 将其“重新解释”为 ISO8859-1 并正常工作使用该字符串的真实编码。 那个双重编码技巧救了我的培根!我想知道为什么它是必需的? 我应该把这些行放在哪里? 我认为双重转换有效,因为它强制进行编码转换(并检查无效字符)。如果源字符串已经以 UTF-8 编码,那么仅调用 .encode('UTF-8') 是无操作的,并且不会运行任何检查。 Ruby Core Documentation for encode。但是,首先将其转换为 UTF-16 会强制运行所有无效字节序列检查,并根据需要进行替换。【参考方案5】:

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

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

我只是使用valid_encoding? 来通过它。我的是一个搜索字段,所以我一遍又一遍地发现同样的怪异之处,所以我使用了类似的东西:只是为了让系统不会崩溃。由于在发送此信息之前我不控制用户体验以自动验证(例如自动反馈说“愚蠢!”),我可以将其接收,剥离并返回空白结果。

【讨论】:

【参考方案6】:

这似乎有效:

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

【讨论】:

【参考方案7】:

虽然 Nakilon 的解决方案有效,但至少在克服错误方面,在我的情况下,我将来自 Microsoft Excel 的这个奇怪的 f-ed 字符转换为在 ruby​​ 中注册为(get this)西里尔字母的 CSV K 在红宝石中是粗体 K。为了解决这个问题,我使用了“iso-8859-1”即。 CSV.parse(f, :encoding => "iso-8859-1"),它把我怪异的 deaky 西里尔字母 K 变成了更易于管理的 /\xCA/,然后我可以用 string.gsub!(/\xCA/, '') 删除它

【讨论】:

再次,我只想指出,虽然 Nakilon(和其他人)的修复是针对源自(哈哈)Cyrillia 的西里尔字符,但此输出是从 xls 转换的 csv 的标准输出!跨度> 【参考方案8】:

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

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

这至少会摆脱我的主要问题的异常

【讨论】:

我将此方法与valid_encoding? 结合使用,这似乎可以检测到何时出现问题。 val.unpack('C*').pack('U*') if !val.valid_encoding?. 这个对我有用。成功地将我的 \xB0 转换回度数符号。即使valid_encoding? 恢复为真,但我仍然检查它是否没有,并使用上面 Amir 的答案去除有问题的字符:string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')。我也尝试过force_encoding 路线,但失败了。 这很棒。谢谢。【参考方案9】:

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

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

【讨论】:

【参考方案10】:

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

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

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

即使在属性值内部,您也必须解码像 amp 这样的 HTML 实体

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

【讨论】:

我很想保留正则表达式,因为它快了大约 10 倍,而且我真的不想正确解析 html,而只想提取链接。我应该可以通过以下方式替换 ruby​​ 中的无效部分: ok_string = bad_string.encode("UTF-8", :invalid => :replace, :undef => :replace) 但这似乎没有工作:(【参考方案11】:

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

【讨论】:

谢谢,但这不是我的问题 :) 我只提取 URL 的主机部分,只点击首页。我的问题是我的输入显然不是 UTF-8 并且 1.9 编码 foo 变得混乱 @Marc Seeger:“我的意见”是什么意思?标准输入、URL 还是页面正文? HTML 可以用 UTF-8 编码:en.wikipedia.org/wiki/Character_encodings_in_HTML 我的输入 = 页面正文 @Eduardo:我知道。我的问题是来自 net/http 的数据似乎不时有错误的编码 网页实际上有错误的编码并不少见。响应标头可能会说它是一种编码,但实际上提供的是另一种编码。

以上是关于ruby 1.9:UTF-8 中的无效字节序列的主要内容,如果未能解决你的问题,请参考以下文章

Ruby/Rails CSV 解析,UTF-8 中的无效字节序列

Ruby on Rails - 来自 Paypal 的参数:utf-8 中的无效字节序列

Paypal IPN:UTF-8 中的无效字节序列

UTF-8 中的无效字节序列 (ArgumentError)

Ruby 1.9:将字节数组转换为具有多字节 UTF-8 字符的字符串

Ruby on Rails“由于bot而导致的UTF-8中无效的字节序列”