Ruby中的SmarterCSV和文件编码问题

3
我正在处理一个似乎采用UTF-16LE编码的文件。如果我运行
File.read(file, :encoding => 'utf-16le')

文件的第一行是:
"<U+FEFF>=\"25/09/2013\"\t18:39:17\t=\"Unknown\"\t=\"+15168608203\"\t\"Message.\"\r\n

如果我使用类似于以下的方式读取文件:
csv_text = File.read(file, :encoding => 'utf-16le')

我收到一个错误提示,内容是:
ASCII incompatible encoding needs binmode (ArgumentError)

如果我将上述内容的编码切换为

csv_text = File.read(file, :encoding => 'utf-8')

我到了代码中的 SmarterCSV 部分,但是出现了一个错误提示。
`=~': invalid byte sequence in UTF-8 (ArgumentError)

完整的代码如下。如果我在Rails控制台中运行它,它可以正常工作,但是如果我使用ruby test.rb运行它,它会给我第一个错误:
require 'smarter_csv'
headers = ["date_of_message", "timestamp_of_message", "sender", "phone_number", "message"]
path = '/path/'
Dir.glob("#{path}*.CSV").each do |file|
  csv_text = File.read(file, :encoding => 'utf-16le')
  File.open('/tmp/tmp_file', 'w') { |tmp_file| tmp_file.write(csv_text) }
  puts 'made it here'
  SmarterCSV.process('/tmp/tmp_file', {
    :col_sep => "\t",
    :force_simple_split => true,
    :headers_in_file => false,
    :user_provided_headers => headers
   }).each do |row|
    converted_row = {}
    converted_row[:date_of_message] = row[:date_of_message][2..-2].to_date
    converted_row[:timestamp] = row[:timestamp]
    converted_row[:sender] = row[:sender][2..-2]
    converted_row[:phone_number] = row[:phone_number][2..-2]
    converted_row[:message] = row[:message][1..-2]
    converted_row[:room] = file.gsub(path, '')
  end
end

更新-05/13/15
最终,我决定将文件字符串编码为UTF-8,而不是更深入地研究SmarterCSV代码。SmarterCSV代码中的第一个问题是它不允许用户在读取文件时指定二进制模式,但在调整源代码以处理该问题后,出现了许多其他与编码相关的问题,其中许多与处理未使用UTF-8编码的文件的各种参数有关。这可能是一种简单的解决方法,但在将所有内容编码为UTF-8之后再将其输入到SmarterCSV中解决了我的问题。

如果您使用内置的CSV类会发生什么? - the Tin Man
与使用SmarterCSV相同的问题。 - AvocadoRivalry
2个回答

2
File.read调用中添加二进制模式。
File.read(file, :encoding => 'utf-16le', mode: "rb")
"b" 二进制文件模式
在Windows上抑制了EOL和CRLF的转换。并且设置外部编码为ASCII-8BIT,除非明确指定。
参考:http://ruby-doc.org/core-2.0.0/IO.html#method-c-read
现在将正确的编码传递给SmarterCSV。
SmarterCSV.process('/tmp/tmp_file', {
:file_encoding => "utf-16le", ...

更新

发现smartercsv不支持二进制模式。在尝试修改代码但未成功后,决定简单的解决方案是将输入转换为UTF-8,这是smartercsv支持的。


这解决了我遇到的第一个问题,但仍然导致SmarterCSV出现问题:=〜':UTF-8中的无效字节序列(ArgumentError) - AvocadoRivalry
添加到答案中以解决您的第二个问题。 - Rots
似乎没有办法为SmarterCSV指定binmode - 错误现在已经变成了我在File.read步骤中最初遇到的错误:ASCII incompatible encoding needs binmode (ArgumentError) - AvocadoRivalry
在 SmarterCSV 的示例中,它有这个 f = File.open(filename, "r:bom|utf-8")。也许将您的写入语句更改为 File.open('/tmp/tmp_file', 'w:bom|utf-16le') - Rots
还是没有运气。我打算就到这儿结束了。编码可能会让人不必要地感到沮丧。 - AvocadoRivalry
显示剩余10条评论

0

很遗憾,您正在使用“平面文件”存储样式,字符编码在读取或写入两端都可能成为问题。

我建议使用类似 str = str.force_encoding("UTF-8") 的方法,看看能否解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接