[英]Is there a way to clean a file of “invalid byte sequence in UTF-8” errors in Ruby?
我有一项服务,可通过客户提供的 XML 提要将数据上传到我们的数据库。 通常这些 XML 文件被声称是 UTF-8 编码的,但它们显然有很多无效的字节序列。 我可以清理这些文件并将它们完美地导入到我们的数据库中,只需在导入之前运行以下 Linux 命令:
tr -cd '^[:print:]' < original.xml > clean.xml
只需运行这个 Linux 命令,我就可以使用 Ruby on Rails 中的 Nokogiri 将所有数据导入到我的数据库中。
问题是我们在Heroku 上部署,我无法使用 Linux 命令预处理文件。 上周我一直在 Internet 上搜索基于 Ruby on Rails 的本机解决方案来解决这个问题,但没有一个有效。 在我完成我尝试过的所有建议之前,这是我的原始代码:
data_source = ARGV[0]
data_file = open data_source
data_string = data_file.read
doc = Nokogiri::XML.parse(data_string)
doc.xpath(".//job").each do |node|
hash = node.element_children.each_with_object(Hash.new) do |e, h|
h[e.name.gsub(/ /,"_").strip.downcase.to_sym] = e.content
data.push(newrow)
end
end
在原始文件上运行它会产生错误:“UTF-8 中的字节序列无效”
以下是我尝试过但都失败的所有有用建议。
使用编码器
Coder.clean!(data_string, "UTF-8")
强制编码
data_string.force_encoding('BINARY').encode('UTF-8', :undef => :replace, :replace => '')
转换为 UTF-16 并返回到 UTF-8
data_string.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '') data_string.encode!('UTF-8', 'UTF-16')
使用valid_encoding?
data_string.chars.select{|i| i.valid_encoding?}.join
没有删除任何字符; 生成“无效字节序列”错误。
在打开文件时指定编码
我实际上编写了一个函数,它尝试所有可能的编码,直到它可以无错误地打开文件并转换为 UTF-8(@file_encodings 是每个可能的文件编码的数组):
@file_encodings.each do |enc|
print "#{enc}..."
conv_str = "r:#{enc}:utf-8"
begin
data_file = File.open(fname, conv_str)
data_string = data_file.read
rescue
data_file = nil
data_string = ""
end
data_string = data_string.encode(enc, :invalid => :replace, :undef => :replace, :replace => "")
unless data_string.blank? print "\n#{enc} detected!\n"
return data_string
end
使用 Regexp 删除不可打印的内容:
data_string.gsub!(/[^[:print:]]/,"") data_string.gsub!(/[[[:cntrl:]&&[^\\n\\r]]/,"")
(我也尝试了包括 /[^a-zA-Z0-9~`!@#$%^&*()-_=+[{]}\\|;:'",<.>/\\?] /)
对于上述所有内容,结果都是相同的......要么发生“无效字节序列”错误,要么在仅读取 4400 行后文件中途被切断。
那么,为什么 Linux "tr" 命令可以完美运行,而这些建议中没有一个可以在 Ruby on Rails 中完成这项工作。
我最终做的非常不优雅,但它完成了工作。 我检查了停止 Nokogiri (row.last) 的每一行并寻找奇怪的字符。 我发现的每一个我都添加到了一个字符类中,然后用 gsub! 把它删除了,就像这样(控制字符不会在这里打印,但你明白了):
data_string.gsub!(/[Crazy Control Characters]/,"")
但我的纯粹主义者坚持认为应该有一个更优雅、更通用的解决方案。
Ruby 2.1 有一个名为String.scrub的新方法,这正是您所需要的。
如果字符串是无效字节序列,则用给定的替换字符替换无效字节,否则返回 self。 如果给出了块,则用块的返回值替换无效字节。
查看文档以获取更多信息。
我在 Stack Overflow 上为其他一些问题找到了这个,这对我来说也很好用。 假设 data_string 是您的 XML:
data_string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
尝试使用 force_encoding("ISO-8859-1") 和 encode("utf-8") 的组合:
data_string.force_encoding("ISO-8859-1").encode("utf-8", replace: nil)
这帮助了我一次。
感谢您的回复。 通过测试各种不同工具的组合,我确实找到了一些可行的方法。 我希望这对其他有同样挫败感的人有所帮助。
data_string.encode!("UTF-8", "UTF-8", invalid: :replace, undef: :replace, replace: "" )
data_string.gsub!(/[[:cntrl:]&&[^\n\r]]/,"")
如您所见,它是“编码”方法和正则表达式的组合,用于删除控制字符(换行符除外)。
我的测试表明我正在导入的文件有两个问题:(1)无效的 UTF-8 字节序列; (2) 不可打印的控制字符,迫使 Nokogiri 在文件结束之前停止解析。 我必须按照这个顺序解决这两个问题,否则 gsub! 抛出“无效字节序列”错误。
请注意,上面代码中的第一行可以用以下任一替换,并获得相同的成功结果:
Coder.clean!(data_string,'UTF-8')
或者
data_string.scrub!("")
这对我来说非常有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.