有没有办法在Ruby中清除“UTF-8中无效字节序列”错误的文件?

在发布到StackOverflow之前我已尝试过所有内容我真的希望有人可以提供帮助,但我非常绝望。

所以,我有一个服务,通过客户提供的XML提要将数据上传到我们的数据库。 这些XML文件通常声称是UTF-8编码,但显然有很多无效的字节序列。 我可以通过在导入之前运行以下Linux命令来清理这些文件并将它们完美地导入我们的数据库:

tr -cd '^[:print:]'  clean.xml 

只需运行这一个Linux命令,我就可以使用Rails中的Nokogiri将所有数据导入我的数据库。

问题是我们正在Heroku上部署,我无法使用Linux命令预处理该文件。 我花了上周的时间在Internet上搜索基于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中的字节序列无效”

以下是我尝试过的所有有用的建议,但都失败了。

  1. 使用编码器

    Coder.clean!(data_string,“UTF-8”)

  2. 强制编码

    data_string.force_encoding(’BINARY’)。encode(’UTF-8’,:undef =>:replace,:replace =>”)

  3. 转换为UTF-16并返回UTF-8

    data_string.encode!(’UTF-16’,’UTF-8’,:invalid =>:replace,:replace =>”)data_string.encode!(’UTF-8’,’UTF-16’)

  4. 使用valid_encoding?

    data_string.chars.select {|我| i.valid_encoding?}。加入

    没有删除任何字符; 生成“无效字节序列”错误。

  5. 在打开文件时指定编码

我实际上编写了一个函数,它可以尝试每种编码,直到它可以无错误地打开文件并转换为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 
  1. 使用Regexp删除非printables:

    data_string.gsub!(/ [^ [:print:]] /,“”)data_string.gsub!(/ [[:cntrl:] && [^ \ n \ r]] /,“”)

(我也试过变种,包括/ [^ a-zA-Z0-9~`!@#$%^&*() – _ = + [{]} \ |;:’“, / \?] /)

对于上述所有情况,结果都是相同的……发生“无效字节序列”错误或在仅读取4400行后中途切断文件。

那么,为什么Linux“tr”命令工作得很好,但这些建议中的任何一个都无法在Rails中完成。

我最终做的是非常不优雅,但完成工作。 我检查了停止Nokogiri(row.last)的每一行并寻找奇怪的角色。 每一个我发现我添加到一个字符类然后gsub!编辑出来,像这样(控制字符不会在这里打印,但你得到的想法):

 data_string.gsub!(/[Crazy Control Characters]/,"") 

但是我的纯粹主义者坚持认为应该有一个更优雅,更通用的解决方案。

(我将所有代码缩进了四个空格,但编辑器似乎没有把它拿起来。)

Ruby 2.1有一个名为String.scrub的新方法,这正是您所需要的。

如果字符串是无效的字节序列,则用给定的替换字符替换无效字节,否则返回self。 如果给出了块,则用块的返回值替换无效字节。

查看文档以获取更多信息。

http://ruby-doc.org/core-2.1.0/String.html#method-i-scrub

我在stackoverflow上找到了一些其他问题,这对我来说也很好。 假设data_string是你的xml:

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

尝试使用force_encoding(“ISO-8859-1”)和编码(“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! 抛出“无效字节序列”错误。

请注意,上面代码中的第一行可以用以下的EITHER替换,但具有相同的成功结果:

 Coder.clean!(data_string,'UTF-8') 

要么

 data_string.scrub!("") 

这对我来说非常合适。