Ruby 中的 SmarterCSV 和文件编码问题
SmarterCSV and file encoding issues in Ruby
我正在处理一个似乎采用 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
更新 - 2015 年 5 月 13 日
最终,我决定将文件字符串编码为 UTF-8,而不是深入研究 SmarterCSV 代码。 SmarterCSV 代码中的第一个问题是它不允许用户在读取文件时指定二进制模式,但在调整源代码以处理该问题后,弹出了无数其他与编码相关的问题,其中许多与处理非 UTF-8 编码文件的各种参数。这可能是最简单的出路,但在将所有内容输入 SmarterCSV 之前将所有内容编码为 UTF-8 解决了我的问题。
不幸的是,您使用的是 'flat-file' 存储方式,字符编码将成为两端(读或写)的问题。
我建议使用类似 str = str.force_encoding("UTF-8")
的东西,看看你是否可以让它工作。
将 binmode 添加到 File.read
调用。
File.read(file, :encoding => 'utf-16le', mode: "rb")
"b" Binary file mode
Suppresses EOL <-> CRLF conversion on Windows. And
sets external encoding to ASCII-8BIT unless explicitly
specified.
参考:http://ruby-doc.org/core-2.0.0/IO.html#method-c-read
现在将正确的编码传递给SmarterCSV
SmarterCSV.process('/tmp/tmp_file', {
:file_encoding => "utf-16le", ...
更新
发现smartercsv
不支持二进制模式。在 OP 尝试修改代码但没有成功之后,决定简单的解决方案是将输入转换为 smartercsv
支持的 UTF-8
。
我正在处理一个似乎采用 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
更新 - 2015 年 5 月 13 日
最终,我决定将文件字符串编码为 UTF-8,而不是深入研究 SmarterCSV 代码。 SmarterCSV 代码中的第一个问题是它不允许用户在读取文件时指定二进制模式,但在调整源代码以处理该问题后,弹出了无数其他与编码相关的问题,其中许多与处理非 UTF-8 编码文件的各种参数。这可能是最简单的出路,但在将所有内容输入 SmarterCSV 之前将所有内容编码为 UTF-8 解决了我的问题。
不幸的是,您使用的是 'flat-file' 存储方式,字符编码将成为两端(读或写)的问题。
我建议使用类似 str = str.force_encoding("UTF-8")
的东西,看看你是否可以让它工作。
将 binmode 添加到 File.read
调用。
File.read(file, :encoding => 'utf-16le', mode: "rb")
"b" Binary file mode Suppresses EOL <-> CRLF conversion on Windows. And sets external encoding to ASCII-8BIT unless explicitly specified.
参考:http://ruby-doc.org/core-2.0.0/IO.html#method-c-read
现在将正确的编码传递给SmarterCSV
SmarterCSV.process('/tmp/tmp_file', {
:file_encoding => "utf-16le", ...
更新
发现smartercsv
不支持二进制模式。在 OP 尝试修改代码但没有成功之后,决定简单的解决方案是将输入转换为 smartercsv
支持的 UTF-8
。