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

在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' 

有什么想法会出错吗?

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

 >> s = "\xBF" => "\xBF" >> s.encoding => # 

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

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

[…]第二种forms返回从src_encoding转码为src_encodingstr副本。

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

 >> foo = s.encode('utf-8', 'binary', :invalid => :replace, :undef => :replace) => " " 

s是从上面认为是UTF-8的"\xBF"

您还可以在s上使用force_encoding强制它为二进制,然后使用双参数encode

 >> s.encoding => # >> s.force_encoding('binary') => "\xBF" >> s.encoding => # >> foo = s.encode('utf-8', :invalid => :replace, :undef => :replace) => " " 

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

 >> "Hello \xBF World!".encode('utf-8', 'binary', :invalid => :replace, :undef => :replace) => "Hello   World!" 

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

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

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

 >> "¡Hace \xBF mucho frío!".chars.select{|i| i.valid_encoding?}.join => "¡Hace mucho frío!" 

同样在Ruby 2.1中,有一种称为scrub的新方法可以解决这个问题

 >> "¡Hace \xBF mucho frío!".scrub => "¡Hace   mucho frío!" >> "¡Hace \xBF mucho frío!".scrub('') => "¡Hace mucho frío!" 

如果您使用显式代码页读取源文本文件,则会修复此问题:

 File.open( 'thefile.txt', 'r:iso8859-1' )