【问题标题】:Ruby read CSV file as UTF-8 and/or convert ASCII-8Bit encoding to UTF-8Ruby 将 CSV 文件读取为 UTF-8 和/或将 ASCII-8Bit 编码转换为 UTF-8
【发布时间】:2011-10-26 06:02:02
【问题描述】:

我使用的是 ruby​​ 1.9.2

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

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

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

元素以 ASCII-8BIT 编码的字符串形式返回(spécifié 变为 sp\xE9cifi\xE9),然后像“spécifié”这样的字符串没有正确保存到我的 MySQL 数据库中。

Yehuda Katz 表示 ASCII-8BIT 是真正的“二进制”数据,这意味着 CSV 不知道如何读取适当的编码。

所以,如果我尝试让 CSV 强制编码如下:

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

我收到以下错误

ArgumentError: invalid byte sequence in UTF-8: 

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

我无法通过这样做将“Non sp\xE9cifi\xE9”转换为“Non spécifié” "Non sp\xE9cifi\xE9".encode("UTF-8")

因为我收到此错误:

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

Katz 指出会发生,因为 ASCII-8BIT 并不是真正的正确字符串“编码”。

问题:

  1. 我可以让 CSV 以适当的编码读取我的文件吗?如果有,怎么做?
  2. 如何将 ASCII-8BIT 字符串转换为 UTF-8 以便在 MySQL 中正确存储?

【问题讨论】:

  • 听起来文件可能不是 UTF-8 编码的;你检查过文件的实际编码了吗?
  • 您的文件未以 UTF-8 编码。 UTF-8 中的 é 应该是 C3 A9,而不是 E9。看起来您正在处理 ISO-8859-1。
  • 我想我明白了:my_ascii_8bit_string.unpack("C*").pack("U*") 似乎有效。
  • @deceze:是的,该文件不是 UTF-8 编码的,但我想通过 ruby​​ 来实现
  • 那么正确的方法是将 CSV 读取为 ISO-8859-1 并使用编码转换函数将结果从 ISO-8859-1 转换为 UTF-8。不幸的是,我的 Ruby 不够好,无法告诉你如何做到这一点。

标签: ruby string encoding csv utf-8


【解决方案1】:

deceze 是对的,即 ISO8859-1 (AKA Latin-1) 编码文本。试试这个:

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

如果这不起作用,您可以使用Iconv 来修复单个字符串,如下所示:

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

如果latin1_string"Non sp\xE9cifi\xE9",那么utf8_string 将是"Non spécifié"。此外,Iconv.iconv 可以一次解开整个数组:

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

使用较新的红宝石,您可以执行以下操作:

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

latin1_string 认为它是 ASCII-8BIT 但实际上是 ISO-8859-1。

【讨论】:

  • 请注意,Ruby 现在希望您使用 String#encode 而不是 iconv
  • @duma:现在好点了吗?我留下了旧的 Iconv 资料,并添加了一个关于使用 force_encodingencode 代替 Iconv 的简短说明。
  • CSV.foreach 为我工作,但我不得不使用 encoding: "iso-8859-1" 而不是 encoding: "ISO8859-1"
【解决方案2】:

使用 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"
  )

【讨论】:

  • 这太棒了。之前,我必须为这个 utf-16 csv 输入一个bomCSV.read('nom_nom_nom.csv', { :headers => true, :col_sep => "\t", :encoding => 'bom|utf-16le'}),否则会抛出错误。现在是:CSV.read('nom_nom_nom.csv', { :headers => true, :col_sep => "\t", external_encoding: 'utf-16', internal_encoding: "utf-8"}) .
【解决方案3】:

我一直在处理这个问题,但没有任何其他解决方案适合我。

诀窍是将冲突的 string 存储在 binary 文件中,然后正常读取文件并使用此 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)

【讨论】:

    猜你喜欢
    • 2020-05-25
    • 1970-01-01
    • 2011-06-26
    • 2011-06-19
    • 1970-01-01
    • 2019-10-31
    • 2013-04-19
    • 2020-10-11
    • 2013-09-12
    相关资源
    最近更新 更多