繁体   English   中英

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

[英]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 中的字节序列无效”

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

  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| i.valid_encoding?}.join

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

  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 删除不可打印的内容:

    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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM