我们允许用户通过 csv 导入数据(使用 ruby 1.9.2,因此它是 fastercsv)。
当然,作为用户数据,它可能没有经过适当的清理。
当我们尝试在 /index 方法中显示数据时,有时会收到错误“UTF-8 中的字节序列无效”,指向我们的 erb,我们在其中显示一个字段 widget.name
当我们进行导入时,我们希望强制传入的数据有效......是否有一个 ruby 运算符将一个字符串映射到一个有效的 utf8 字符串,例如,类似
goodstring = badstring.no_more_invalid_bytes
“坏”数据的一个示例是看起来像连字符但不是常规 ascii 连字符的 char。我们更愿意将非 utf-8 字符映射到合理的 ascii 等价物(umlat-u 以 u 为例)但我们可以简单地将字符剥离到。
因为这是在导入大量数据时,它需要是一个快速的内置运算符,希望......
注意:这里是数据示例。该文件来自 windows 并且是 8bit ascii。当我们导入它并在我们的 erb 中显示 widget.name.inspect(而不是 widget.name)时,我们得到:“Chains \x96 Accessories”
所以数据的一个例子是一个“连字符”,它实际上是 8 位代码 96。
--- 当我们将 csv 解析更改为分配 fldval = d.encode('UTF-8') 时,它会引发以下错误:
Encoding::UndefinedConversionError in StoresController#importfinderitems
"\x96" from ASCII-8BIT to UTF-8
我们正在寻找的是一种简单的方法来强制它是有效的 utf8,而不管原始类型如何,即使我们只是简单地去除非 ascii。
虽然不像强制编码那样“好”,但这会稍微花费我们的导入时间: d.to_s.strip.gsub(/\P{ASCII}/, '') 谢谢你,Mladen!
\x96
是一个连字符在 Windows-1251 编码中。如果您确定所有传入数据都在该特定数据中,则可以将其转换为 UTF-8。
Ruby 1.9 CSV 具有与 m17n 一起使用的新解析器。解析器使用字符串中 IO 对象的编码。以下方法:::foreach, ::open, ::read, and ::readlines
可以采用可选选项 :encoding
,您可以指定编码。
例如:
CSV.read('/path/to/file', :encoding => 'windows-1251:utf-8')
将所有字符串转换为 UTF-8。
您也可以使用更标准的编码名称“ISO-8859-1”
CSV.read('/..', {:headers => true, :col_sep => ';', :encoding => 'ISO-8859-1'})
CSV.parse(File.read('/path/to/csv').scrub)
我回答了一个类似的问题,该问题涉及在 1.9.2 中使用非 UTF-8 编码读取外部文件。我认为这个答案会对您有很大帮助:Character Encoding issue in Rails v3/Ruby 1.9.2
请注意,您需要知道源编码才能可靠地转换它。有像我在其他答案中链接到的那样的库,可以帮助您确定这一点。
此外,如果您不从文件加载数据,您可以很容易地转换 1.9.2 中的字符串编码:
'string'.encode('UTF-8')
但是,您很少使用另一种编码构建字符串,如果可能,最好在将其读取到您的环境中时对其进行转换。
read
方法接收可选的 :encoding
参数,因此您可以使用类似 CSV.read('/path/to/file', :encoding => 'windows-1251:utf-8')
的方式在加载时自动对文件进行转码。
Ruby 1.9 可以通过无效检测和替换来更改字符串编码:
str = str.encode('UTF-8', :invalid => :replace)
对于不寻常的字符串,例如从未知编码的文件加载的字符串,使用#encode 代替正则表达式、#gsub 或#delete 是明智的,因为这些都需要解析字符串——但如果字符串被破坏,它无法解析,因此这些方法失败。
如果您收到这样的消息:
error ** from ASCII-8BIT to UTF-8
然后您可能正在尝试转换已经在 UTF-8 中的二进制字符串,并且您可以强制使用 UTF-8:
str.force_encoding('UTF-8')
如果您知道原始字符串不是二进制 UTF-8,或者如果输出字符串包含非法字符,请阅读 Ruby 编码音译。
如果您使用的是 Rails,您可以尝试使用以下方法修复它
'Your string with strange stuff #@~'.mb_chars.tidy_bytes
它会删除您无效的 utf-8 字符并将其替换为有效字符。更多信息:https://apidock.com/rails/String/mb_chars
将 CSV 文件上传到 Google Docs 电子表格,然后将其重新下载为 CSV 文件。导入,瞧! (在我的情况下工作)
据推测,谷歌将其转换为想要的格式..
来源:Excel to CSV with UTF-8 Encoding
正如其他人所提到的,scrub 可以很好地在 Ruby 2.1+ 中清理它。如果您有一个大文件,您可能不想将整个内容读入内存,因此您可以像这样使用擦洗:
data = IO::read(file_path).scrub("")
CSV.parse(data, :col_sep => ',', :headers => true) do |row|
puts row
end
我正在使用 MAC,我遇到了同样的错误:
rescue in parse:Invalid byte sequence in UTF-8 in line 1 (CSV::MalformedCSVError)
我添加了解决我的错误的 :encoding => 'ISO-8859-1'
,并且可以读取 csv 文件。
results = CSV.read("query_result.csv",{:headers => true, :encoding => 'ISO-8859-1'})
:headers => true
:如果设置为 :first_row 或 true,则 CSV 文件的初始行将被视为一行标题。如果设置为数组,则内容将用作标题。如果设置为字符串,则字符串将通过调用 ::parse_line 来运行,并使用与此实例相同的 :col_sep、:row_sep 和 :quote_char 来生成标题数组。此设置导致 #shift 以 CSV::Row 对象而不是数组的形式返回行,并且 #read 以返回 CSV::Table 对象而不是数组数组的形式返回。
irb(main):024:0> rows = CSV.new(StringIO.new("a,b,c\n1,2,3"), headers: true)
=> <#CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"" headers:true>
irb(main):025:0> rows = CSV.new(StringIO.new("a,b,c\n1,2,3"), headers: true).to_a
=> [#<CSV::Row "a":"1" "b":"2" "c":"3">]
irb(main):026:0> rows.first['a']
=> "1"
在上面的示例中,您可以清楚地看到这也使我们能够将数据用作散列。使用 headers: true
时唯一需要注意的是它不允许任何重复的标头,因为键在散列中是唯一的。
只做这个
anyobject.to_csv(:encoding => 'utf-8')
不定期副业成功案例分享
:encoding => 'ISO-8859-1'
是否可以与:encoding => 'windows-1251:utf-8'
交换?