ChatGPT解决这个技术问题 Extra ChatGPT

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

我正在用 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 方库不会崩溃,而不是我对这里和那里的一句话所做的更多。

M
Mark Swardstrom

在 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

对于一些有问题的输入,我还使用了从 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 会强制运行所有无效字节序列检查,并根据需要进行替换。
A
Amir Raminfar

接受的答案或其他答案对我有用。我找到了建议的 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??? {!@#$%^&*()_+}< /p>

\xEF\xBF\xBD \xef\xbf\x9c
\xc2\x90
\xc2\x90"
第二个参数“二进制”是为了什么?
M
Marc Seeger

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

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 路线,但失败了。
这很棒。谢谢。
R
Ranjithkumar Ravi

尝试这个:

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

我的情况的最佳答案!谢谢
C
Community

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

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

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

即使在属性值内部,您也必须解码 HTML 实体,如 amp

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


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

S
Spajus

这似乎有效:

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

N
Nakilon

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

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

b
boulder_ruby

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


同样,我只想指出,虽然 Nakilon(和其他人)的修复是针对源自(哈哈)Cyrillia 的西里尔字符,但此输出是从 xls 转换的 csv 的标准输出!
A
Adrian

在使用 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 还是页面正文?
我的输入 = 页面正文 @Eduardo:我知道。我的问题是来自 net/http 的数据似乎不时有错误的编码
网页实际上具有错误的编码并不少见。响应标头可能会说它是一种编码,但实际上是提供另一种编码。
p
pjammer

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

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

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