簡體   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