ChatGPT解决这个技术问题 Extra ChatGPT

当我们导入 csv 数据时,如何消除“UTF-8 中的无效字节序列”

我们允许用户通过 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!

你在使用 String#force_encoding 吗?您还可以粘贴一个生成错误的示例字符串吗?
一个非常粗略(但简单)的解决方案是从输入中删除所有非 ASCII 字符。这对你有用吗?
顺便说一句,\x96 是一个连字符在 Windows-1251 编码中。如果您确定所有传入数据都在该特定数据中,则可以将其转换为 UTF-8。
剥离所有非ASCII很好,我们该怎么做?不,我们不确定输入格式......许多用户,许多可能不同的东西,因此我们需要“强制它”
使用正则表达式进行剥离,感谢您的建议。宁愿强制编码,但这会起作用!

s
siegy22

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'})

'windows-1251:utf-8' 是什么意思?
@hmak 意思是“从 windows-1251 字符编码转换为 utf-8 编码”
谢谢一大堆人 ":encoding => 'windows-1251:utf-8' " 成功了
有没有办法强制任何编码,而不仅仅是 windows-1251,到 UTF-8?像 '????:utf-8' 之类的东西?即我只想确保我导入的 CSV 始终是 UTF8,并不关心它最初是什么编码。
您不应该说“您也可以使用”,然后显示与您所指的行完全不同的行。 :encoding => 'ISO-8859-1' 是否可以与 :encoding => 'windows-1251:utf-8' 交换?
B
Bill Lipa
CSV.parse(File.read('/path/to/csv').scrub)

值得注意的是这是 Ruby 2.1+
C
Community

我回答了一个类似的问题,该问题涉及在 1.9.2 中使用非 UTF-8 编码读取外部文件。我认为这个答案会对您有很大帮助:Character Encoding issue in Rails v3/Ruby 1.9.2

请注意,您需要知道源编码才能可靠地转换它。有像我在其他答案中链接到的那样的库,可以帮助您确定这一点。

此外,如果您不从文件加载数据,您可以很容易地转换 1.9.2 中的字符串编码:

'string'.encode('UTF-8')

但是,您很少使用另一种编码构建字符串,如果可能,最好在将其读取到您的环境中时对其进行转换。


那个解释。虽然对我理解出了什么问题非常有帮助,但似乎并没有解决我如何解决这个问题。我们无法控制用户尝试导入的内容,我们只需要对其进行清理。更糟糕的是,使用 fastcsv 我们不做明确的“打开”,我们只是做一个读取“csv_data = CSV.read(params[:upload][:file].tempfile)”
fwiw,我们可以很好地读取文件,对其进行 csv 解析,将其存储在 dbase 中(好吧,至少本地 dbase 还没有在 heroku 上尝试过相同的测试文件)。然而 /index 抱怨非 utf 序列。
Ruby 1.9 具有内置的 CSV 类,它的 read 方法接收可选的 :encoding 参数,因此您可以使用类似 CSV.read('/path/to/file', :encoding => 'windows-1251:utf-8') 的方式在加载时自动对文件进行转码。
@MladenJabnović,这可以解决我遇到的问题!谢谢!
@Mladen Jablanović 非常感谢!!!!这让我很伤心,无法弄清楚该文件来自蹩脚的 Windows 机器:)
j
joelparkerhenderson

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 编码音译。


实际上,这看起来可能会有很大帮助 - 感谢您的回复。迄今为止,我们只是在问题发生时手动重新处理文件,因此这实际上可能会解决它。
Hai 当我给出上面的代码时,它给出了这个错误**“\x92”从 ASCII-8BIT 到 UTF-8 **
@Gowri 我为你添加了更多信息
d
dom

如果您使用的是 Rails,您可以尝试使用以下方法修复它

'Your string with strange stuff #@~'.mb_chars.tidy_bytes

它会删除您无效的 utf-8 字符并将其替换为有效字符。更多信息:https://apidock.com/rails/String/mb_chars


C
Community

将 CSV 文件上传到 Google Docs 电子表格,然后将其重新下载为 CSV 文件。导入,瞧! (在我的情况下工作)

据推测,谷歌将其转换为想要的格式..

来源:Excel to CSV with UTF-8 Encoding


A
Andy Fraley

正如其他人所提到的,scrub 可以很好地在 Ruby 2.1+ 中清理它。如果您有一个大文件,您可能不想将整个内容读入内存,因此您可以像这样使用擦洗:

data = IO::read(file_path).scrub("")
CSV.parse(data, :col_sep => ',', :headers => true)  do |row|
   puts row
end

这如何避免将文件读入内存? (提示:它没有)
S
Sumeet Raina

我正在使用 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 时唯一需要注意的是它不允许任何重复的标头,因为键在散列中是唯一的。


D
Daniel Antonio Nuñez Carhuayo

只做这个

anyobject.to_csv(:encoding => 'utf-8')

我在 win 32 位上看到了问题,但是,在 64 位中,附加参数不是必需的