【问题标题】:String#encode not fixing "invalid byte sequence in UTF-8" errorString#encode 未修复“UTF-8 中的无效字节序列”错误
【发布时间】:2012-07-07 16:30:52
【问题描述】:

我知道关于这个错误有多个类似的问题,我尝试了很多都没有运气。我遇到的问题涉及字节 \xA1 并且正在抛出

ArgumentError: UTF-8 中的无效字节序列

我尝试了以下方法但没有成功:

"\xA1".encode('UTF-8', :undef => :replace, :invalid => :replace,
    :replace => "").sub('', '')
"\xA1".encode('UTF-8', :undef => :replace, :invalid => :replace,
    :replace => "").force_encoding('UTF-8').sub('', '')
"\xA1".encode('UTF-8', :undef => :replace, :invalid => :replace,
    :replace => "").encode('UTF-8').sub('', '')

每一行都会为我抛出错误。我做错了什么?

更新:

上述行仅在 IRB 中失败。但是,我修改了我的应用程序以使用相同的 String#encode 方法和参数对 CVS 文件的行进行编码,并且在从 file 读取该行时遇到相同的错误(注意:如果您使用 IO 对同一字符串执行操作)。

bad_line = "col1\tcol2\tbad\xa1"

bad_line.sub('', '') # does NOT fail
puts bad_line # => col1 col2    bad?

tmp = Tempfile.new 'foo' # write the line to a file to emulate real problem
tmp.puts bad_line
tmp.close

tmp2 = Tempfile.new 'bar'

begin
  IO.foreach tmp.path do |line|
    line.encode!('UTF-8', :undef => :replace, :invalid => :replace, :replace => "")
    line.sub('', '') # fail: invalid byte sequence in UTF-8
    tmp2.puts line
  end
  tmp2.close

  # this would fail if the above error didn't halt execution
  CSV.foreach(tmp2.path) do |row|
    puts row.inspect # fail: invalid byte sequence in UTF-8
  end
ensure
  tmp.unlink
  tmp2.close
  tmp2.unlink
end

【问题讨论】:

  • 这些行都不会在我的机器上使用 MRI 1.9.3p125 引发错误。
  • 我在使用 MRI 1.9.3p194 的 IRB 中得到这些错误。

标签: ruby unicode utf-8 ruby-1.9


【解决方案1】:

似乎ruby认为字符串编码已经是utf8了,所以当你这样做时

line.encode!('UTF-8', :undef => :replace, :invalid => :replace, :replace => "")

它实际上并没有做任何事情,因为目标编码与当前编码相同(至少这是我对 transcode.c 中代码的解释)

这里真正的问题是,您的起始数据在某些不是 utf-8 的编码中是否有效,或者这是否是应该是 utf-8 但其中有一些您想要丢弃的缺陷的数据。

在第一种情况下,正确的做法是告诉 ruby​​ 这个编码是什么。您可以在打开文件时执行此操作

File.open('somefile', 'r:iso-8859-1')

将打开文件,将其内容解释为 iso-8859-1

您甚至可以让 ruby​​ 为您转码

File.open('somefile', 'r:iso-8859-1:utf-8')

将以 iso-8859-1 格式打开文件,但当您从中读取数据时,字节将为您转换为 utf-8。

你也可以调用force_encoding告诉ruby一个字符串的编码是什么(这根本不会修改字节,它只是告诉ruby如何解释它们)。

在第二种情况下,你只想将任何讨厌的东西转储到你的 utf-8 中,你不能像你一样只调用encode!,因为这是一个空操作。在 ruby​​ 2.1 及更高版本中,您可以使用String#scrub,在之前的版本中您可以这样做

line.encode!('UTF-16', :undef => :replace, :invalid => :replace, :replace => "")
line.encode!('UTF-8')

我们首先转换为 utf-16。由于这是一种不同的编码,ruby 实际上会替换我们的无效序列。然后我们可以转换回 utf-8。这不会丢失任何额外的数据,因为 utf-8 和 utf-16 只是编码相同底层字符集的两种不同方式。

【讨论】:

  • 谢谢。将其编码为 UTF-16 然后返回 UTF-8 完成了我需要的操作。输入文件的编码没有被源定义好,所以我不能使用第一个选项。
  • 这太棒了,非常感谢@Frederick Cheung
  • 嗨,谢谢,这是仅在低于 2.1 的版本中出现的问题吗?如果是,我会立即提供更新
  • 不,只是注意到 ruby​​ 2.1 包含一个额外的方法来处理这个
【解决方案2】:

也许您正在 IRB 中运行此代码。我在使用 IRB 时遇到了很多编码问题。在这种情况下,请尝试将此代码保存为 .rb 文件并从命令行运行代码。

【讨论】:

  • 是的,你是对的。在实际应用程序中发现错误(使用 CVS#read 解析 CVS 文件)后,我试图在 IRB 中解决此问题。在阅读之前,我会考虑将文件编码为 UTF-8。
  • 很高兴听到。如果这解决了您的问题,请考虑接受我的回答。
  • 当从文件中读取包含该字节的行(而不仅仅是硬编码字符串)时,文件中似乎存在问题。我用一个更好的例子修改了我原来的帖子。
猜你喜欢
  • 1970-01-01
  • 2013-03-10
  • 1970-01-01
  • 2012-04-20
  • 2016-01-08
  • 1970-01-01
  • 2015-07-04
  • 2012-03-25
相关资源
最近更新 更多