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

Cha*_*eak 1 ruby encoding encode ruby-on-rails utf-8

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

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

tr -cd '^[:print:]' < original.xml > clean.xml
Run Code Online (Sandbox Code Playgroud)

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

问题是我们正在Heroku上部署,我无法使用Linux命令预处理该文件.我花了最后一周在互联网上搜索基于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
Run Code Online (Sandbox Code Playgroud)

在原始文件上运行此操作会产生错误:"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
Run Code Online (Sandbox Code Playgroud)
  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]/,"")
Run Code Online (Sandbox Code Playgroud)

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

(我已经将我的所有代码缩进了四个空格,但编辑器似乎没有采用它.)

Ser*_*yol 7

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

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

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

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