Ruby将CSV文件读取为UTF-8和/或将ASCII-8Bit编码转换为UTF-8

我使用ruby1.9.2

我试图parsing一个包含一些法语单词(例如spécifié) 的CSV文件 ,并将内容放在MySQL数据库中。

当我从CSV文件中读取行时,

file_contents = CSV.read("csvfile.csv", col_sep: "$") 

元素返回为ASCII-8BIT编码的string(spécifié变成sp \ xE9cifi \ xE9),然后像“spécifié”这样的string不能正确保存到我的MySQL数据库中。

耶胡达卡茨说,ASCII-8BIT是真正的“二进制”的数据,这意味着CSV不知道如何读取适当的编码。

所以,如果我试图使CSV像这样编码:

file_contents = CSV.read("csvfile.csv", col_sep: "$", encoding: "UTF-8")

我得到以下错误

 ArgumentError: invalid byte sequence in UTF-8: 

如果我回到我的原始ASCII-8BIT编码string,并检查我的CSV读取为ASCII-8BIT的string,它看起来像这个“Non sp \ xE9cifi \ xE9”而不是“Nonspécifié”。

我无法使用"Non sp\xE9cifi\xE9".encode("UTF-8")将“Non sp \ xE9cifi \ xE9”转换为“Nonspécifié”。

因为我得到这个错误:

Encoding::UndefinedConversionError: "\xE9" from ASCII-8BIT to UTF-8

Katz指出会发生这种情况,因为ASCII-8BIT并不是真正的string“编码”。

问题:

  1. 我可以得到CSV来读取我的文件在适当的编码? 如果是这样,怎么样?
  2. 如何将ASCII-8BITstring转换为UTF-8以在MySQL中正确存储?

欺骗是正确的,那就是ISO8859-1(AKA Latin-1)编码的文本。 尝试这个:

 file_contents = CSV.read("csvfile.csv", col_sep: "$", encoding: "ISO8859-1") 

如果这样做不起作用,可以使用Iconv来修改单个string,如下所示:

 require 'iconv' utf8_string = Iconv.iconv('utf-8', 'iso8859-1', latin1_string).first 

如果"Non sp\xE9cifi\xE9""Non sp\xE9cifi\xE9" ,那么utf8_string将是"Non spécifié" 。 而且, Iconv.iconv可以一次Iconv.iconv整个数组的Iconv.iconv

 utf8_strings = Iconv.iconv('utf-8', 'iso8859-1', *latin1_strings) 

随着更新的ruby,你可以做这样的事情:

 utf8_string = latin1_string.force_encoding('iso-8859-1').encode('utf-8') 

其中latin1_string认为它是在ASCII-8BIT,但真正在ISO-8859-1。

随着ruby> = 1.9,你可以使用

 file_contents = CSV.read("csvfile.csv", col_sep: "$", encoding: "ISO8859-1:utf-8") 

ISO8859-1:utf-8的含义是:csv文件是ISO8859-1编码的,但是把内容转换为utf-8

如果你喜欢更详细的代码,你可以使用:

 file_contents = CSV.read("csvfile.csv", col_sep: "$", external_encoding: "ISO8859-1", internal_encoding: "utf-8" ) 

我一直在处理这个问题一段时间,而没有任何其他解决scheme为我工作。

诀窍就是将冲突string存储在二进制文件中,然后正常读取文件并使用此string来提供CSV模块:

 tempfile = Tempfile.new("conflictive_string") tempfile.binmode tempfile.write(conflictive_string) tempfile.close cleaned_string = File.read(tempfile.path) File.delete(tempfile.path) csv = CSV.new(cleaned_string)