如何全局忽略UTF-8字符串中的无效字节序列?

fot*_*nus 17 ruby encoding ruby-on-rails

自Rails版本1以来,我有一个Rails应用程序从迁移中幸存下来,我想忽略它上面的所有无效字节序列,以保持向后兼容性.

我不知道输入编码.

例:

> "- Men\xFC -".split("n")
ArgumentError: invalid byte sequence in UTF-8
    from (irb):4:in `split'
    from (irb):4
    from /home/fotanus/.rvm/rubies/ruby-2.0.0-rc2/bin/irb:16:in `<main>'
Run Code Online (Sandbox Code Playgroud)

我可以通过使用以下内容在一行中克服此问题,例如:

> "- Men\xFC -".unpack("C*").pack("U*").split("n")
 => ["- Me", "ü -"] 
Run Code Online (Sandbox Code Playgroud)

但是,我想始终忽略无效的字节序列并禁用此错误.在Ruby本身或Rails中.

Dav*_*son 16

我不认为你可以在没有太大困难的情况下全局关闭UTF-8检查.我将专注于修复进入应用程序的所有字符串,在它们进入的边界(例如,当您查询数据库或接收HTTP请求时).

让我们假设进来的字符串有BINARY(也就是ASCII-8BIT编码).这可以这样模拟:

s = "Men\xFC".force_encoding('BINARY')  # => "Men\xFC"
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用String#encode将它们转换为UTF-8,并用UTF-8替换字符替换任何未定义的字符:

s = s.encode("UTF-8", invalid: :replace, undef: :replace)  # => "Men\uFFFD"
s.valid_encoding?  # => true
Run Code Online (Sandbox Code Playgroud)

不幸的是,上面的步骤最终会破坏很多UTF-8代码点,因为它们中的字节不会被识别.如果您有一个三字节的UTF-8字符,如"\ uFFFD",它将被解释为三个单独的字节,每个字节将被转换为替换字符.也许你可以这样做:

def to_utf8(str)
  str = str.force_encoding("UTF-8")
  return str if str.valid_encoding?
  str = str.force_encoding("BINARY")
  str.encode("UTF-8", invalid: :replace, undef: :replace)
end
Run Code Online (Sandbox Code Playgroud)

这是我能想到的最好的.不幸的是,我不知道告诉Ruby将字符串视为UTF-8并只替换所有无效字节的好方法.


小智 6

在ruby 2.0中你可以使用String#b方法,这是String#force_encoding("BINARY")的一个简短别名