Ruby String.encode仍然提供"UTF-8中的无效字节序列"

dre*_*lis 7 ruby encoding

在IRB,我正在尝试以下方面:

1.9.3p194 :001 > foo = "\xBF".encode("utf-8", :invalid => :replace, :undef => :replace)
 => "\xBF" 
1.9.3p194 :002 > foo.match /foo/
ArgumentError: invalid byte sequence in UTF-8
from (irb):2:in `match'
Run Code Online (Sandbox Code Playgroud)

有什么想法会出错吗?

mu *_*ort 22

我猜它"\xBF"已经认为它是用UTF-8编码的,所以当你打电话时encode,它认为你正在尝试用UTF-8编码UTF-8字符串而什么都不做:

>> s = "\xBF"
=> "\xBF"
>> s.encoding
=> #<Encoding:UTF-8>
Run Code Online (Sandbox Code Playgroud)

\xBF是无效的UTF-8,所以这当然是胡说八道.但是如果你使用以下三种形式encode:

encode(dst_encoding,src_encoding [,options])→str

[...]第二种形式返回的副本str从转码src_encodingdst_encoding.

您可以通过告知encode忽略字符串认为其编码是什么来强制解决问题,并将其视为二进制数据:

>> foo = s.encode('utf-8', 'binary', :invalid => :replace, :undef => :replace)
=> "?"
Run Code Online (Sandbox Code Playgroud)

哪里s"\xBF"说认为它是UTF-8从上面.

您也可以使用force_encodingon s强制它为二进制,然后使用两个参数encode:

>> s.encoding
=> #<Encoding:UTF-8>
>> s.force_encoding('binary')
=> "\xBF"
>> s.encoding
=> #<Encoding:ASCII-8BIT>
>> foo = s.encode('utf-8', :invalid => :replace, :undef => :replace)
=> "?"
Run Code Online (Sandbox Code Playgroud)


Eri*_*ham 5

如果您只使用 ascii 字符,则可以使用

>> "Hello \xBF World!".encode('utf-8', 'binary', :invalid => :replace, :undef => :replace)
=> "Hello ? World!"
Run Code Online (Sandbox Code Playgroud)

但是如果我们对 ascii 中无效的有效 UTF8 字符使用相同的方法会发生什么

>> "¡Hace \xBF mucho frío!".encode('utf-8', 'binary', :invalid => :replace, :undef => :replace)
=> "??Hace ? mucho fr??o!"
Run Code Online (Sandbox Code Playgroud)

哦哦!我们希望 frío 保持口音。这是一个保留有效 UTF8 字符的选项

>> "¡Hace \xBF mucho frío!".chars.select{|i| i.valid_encoding?}.join
=> "¡Hace  mucho frío!"
Run Code Online (Sandbox Code Playgroud)

同样在 Ruby 2.1 中有一个新方法叫做scrub解决这个问题

>> "¡Hace \xBF mucho frío!".scrub
=> "¡Hace ? mucho frío!"
>> "¡Hace \xBF mucho frío!".scrub('')
=> "¡Hace  mucho frío!"
Run Code Online (Sandbox Code Playgroud)