如何在Ruby中创建一个“坏编码”的字符串?

7
我在生产环境中有一个文件,但我无法访问。当使用Ruby脚本加载该文件时,对其内容执行正则表达式会出现“ArgumentError => invalid byte sequence in UTF-8”的错误。
我相信我可以根据此处所有要点的答案进行修复:ruby 1.9: invalid byte sequence in UTF-8。请注意保留HTML标记。
# Remove all invalid and undefined characters in the given string
# (ruby 1.9.3)
def safe_str str

  # edited based on matt's comment (thanks matt)
  s = str.encode('utf-16', 'utf-8', invalid: :replace, undef: :replace, replace: '')
  s.encode!('utf-8', 'utf-16')
end

然而,我现在想要构建我的rspec来验证代码是否有效。我无法访问导致问题的文件,因此我想通过程序创建一个带有错误编码的字符串。

我尝试了各种变化,例如:

bad_str = (100..1000).to_a.inject('') {|s,c| s << c; s}
bad_str.length.should > safe_str(bad_str).length

或者,
bad_str = (100..1000).to_a.pack(c*)
bad_str.length.should > safe_str(bad_str).length

但长度总是相同的。我也尝试了不同的字符范围,而不总是100到1000。

有没有建议在Ruby 1.9.3脚本中构建具有无效编码的字符串?


你的错误字符串是否触发了原始的“无效字节序列”异常?也许它们真的是有问题的,但是由于某些原因,safe_str 没有捕获到它。 - Hew Wolff
感谢@HewWolff,我还没有部署它。我想让我的测试行为正常(根据Matt的评论,这是一件好事)。 - GSP
5个回答

5
大量的单字节字符串会生成无效的UTF-8字符串,以0x80开头。所以128.chr应该可以使用。

谢谢。我忘记了Integer上的chr方法。 - GSP

3
您的safe_str方法(目前)实际上不会对字符串做任何事情,它是一个无操作。 Ruby 1.9.3上String#encode的文档说:

请注意,从编码enc转换为相同的编码enc是一个无操作,即返回接收器而没有任何更改,并且没有引发任何异常,即使存在无效字节也是如此。

这对于2.0.0(补丁级别247)的当前版本是正确的,但是针对Ruby主干的
最近的提交更改了此内容,并且还介绍了一个scrub方法,几乎可以满足您的需求。
在新版本的Ruby发布之前,您需要将文本字符串往返于另一种编码格式,以清除它,就像在您链接到的问题的此答案中的第二个示例中所示,如下所示:
def safe_str str
  s = str.encode('utf-16', 'utf-8', invalid: :replace, undef: :replace, replace: '')
  s.encode!('utf-8', 'utf-16')
end

请注意,您试图创建一个无效字符串的第一个示例将无法工作:
bad_str = (100..1000).to_a.inject('') {|s,c| s << c; s}
bad_str.valid_encoding? # => true

根据文档,如果对象是一个整数,则被视为一个代码点,并在连接之前转换为字符。

这样您将始终获得有效的字符串。

您的第二种方法使用pack将创建一个具有编码ASCII-8BIT的字符串。然后,您可以使用force_encoding更改此编码,从而创建一个具有无效编码的UTF-8字符串。

bad_str = (100..1000).to_a.pack('c*').force_encoding('utf-8')
bad_str.valid_encoding? # => false

谢谢。一直让我困扰的是force_encoding这一步骤。 - GSP
P.S. 我希望我能够使用 Ruby 2。scrub 方法正是我所需要的。 - GSP

3

尝试使用s = "hi \255"

s.valid_encoding?
# => false

1
以下示例可用于测试目的:
describe TestClass do
  let(:non_utf8_text) { "something\255 english." }

  it 'is not raise error on invalid byte sequence string' do
    expect(non_utf8_text).not_to be_valid_encoding
    expect { subject.call(non_utf8_text) }.not_to raise_error
  end
end

感谢Iwan B.提供的"\255"建议。

0
在我编写的规范测试中,我没有找到解决这个坏编码的方法:
Period%Basics
%B字符串始终会产生“ArgumentError:UTF-8中的无效字节序列”。

我不确定你所说的“%”是什么意思?你是用它来表示像“Period\xBasics”这样的十六进制值吗? - GSP
我甚至都没有去检查这个值映射到什么或代表什么。我是通过检查Airbrake异常并且发现这个坏字符串来自于一个GET参数而意识到这个字符串的存在。我尝试了各种方法来捕获或修复这个异常,但是没有成功。 - parhamr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接