如何在不转换为不同编码的情况下替换Ruby中的UTF-8错误?

Mat*_*att 10 ruby string unicode encoding utf-8

要将字符串转换为UTF-8并替换所有编码错误,您可以执行以下操作:

str.encode('utf-8', :invalid=>:replace)
Run Code Online (Sandbox Code Playgroud)

唯一的问题是,如果str已经是UTF-8,它就不起作用,在这种情况下,任何错误仍然存​​在:

irb> x = "foo\x92bar".encode('utf-8', :invalid=>:replace)
=> "foo\x92bar"
irb> x.valid_encoding?
=> false
Run Code Online (Sandbox Code Playgroud)

引用Ruby Docs:

请注意,从编码enc到相同编码的转换enc是无操作,即接收器在没有任何更改的情况下返回,并且即使存在无效字节也不会引发异常.

显而易见的解决方法是首先转换为不同的Unicode编码,然后再转换回UTF-8:

str.encode('utf-16', :invalid=>:replace).encode('utf-8')
Run Code Online (Sandbox Code Playgroud)

例如:

irb> x = "foo\x92bar".encode('utf-16', :invalid=>:replace).encode('utf-8')
=> "foo?bar"
irb> x.valid_encoding?
=> true
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来做到这一点而不转换为虚拟编码?

mat*_*att 14

Ruby 2.1添加了一个String#scrub执行您想要的方法:

2.1.0dev :001 > x = "foo\x92bar"
 => "foo\x92bar" 
2.1.0dev :002 > x.valid_encoding?
 => false 
2.1.0dev :003 > y = x.scrub
 => "foo?bar" 
2.1.0dev :004 > y.valid_encoding?
 => true 
Run Code Online (Sandbox Code Playgroud)

相同的提交也会改变行为,encode以便在源和目标编码相同时它起作用:

2.1.0dev :005 > x = "foo\x92bar".encode('utf-8', :invalid=>:replace)
 => "foo?bar" 
2.1.0dev :006 > x.valid_encoding?
 => true 
Run Code Online (Sandbox Code Playgroud)

据我所知,在2.1之前没有内置的方法来执行此操作(否则scrub将不需要),因此您需要使用一些解决方法技术,直到2.1发布并且您可以升级.


tih*_*hom 6

尝试这个:

 "foo\x92bar".chars.select(&:valid_encoding?).join
  # => "foobar"
Run Code Online (Sandbox Code Playgroud)

或者更换

"foo\x92bar".chars.map{|c| c.valid_encoding? ? c : "?"}.join
 # =>  "foo?bar"
Run Code Online (Sandbox Code Playgroud)