修复MySQL不正确的字符串编码

我正在从一个没有为Unicode设置的mysql数据库中读取字符串。

Ruby将字符串作为七大洋但我知道正确的版本应该是七大洋 。 “错误”字符串被编码为UTF-8,因为Ruby不知道它有错误。 我已经尝试在受损的字符串上强制执行每个编码,但没有任何作用。 我有一种感觉,我可以通过摆弄这些位来做到这一点,但我甚至不知道从哪里开始。

我认为没有任何信息丢失,因为错误的字符串实际上比正确的字符串有更多的字节。 我不认为Ruby是这里的罪魁祸首,因为当我在Ruby之外查看表时,字符串看起来也很糟糕 – 所以我希望能够消除MySQL已经造成的损害。

您可以使用以下构造来还原编码:

 "wrong_string".encode(Encoding::SOME_ENCODING).force_encoding('utf-8') 

我尝试了所有可能的编码来检测正确的编码:

 Encoding.constants.each_with_object({}) do |encoding_name, result| value = "七大洋".encode(Encoding.const_get encoding_name).force_encoding('utf-8') rescue nil result[encoding_name] = value if value == "七大洋" end.keys #=> [:Windows_1252, :WINDOWS_1252, :CP1252, :Windows_1254, :WINDOWS_1254, :CP1254] 

因此,要将您的字符串转换为七大洋您可以使用上面的任何编码。

亚历山大指出了我的主要错误(你需要encode然后使用force_encoding来找到正确的编码)。 该字符串确实编码为CP1252!

最好的解决方案是从MySQL读取二进制文件然后强制编码:

 client = Mysql2::Client.new(opts.merge encoding: 'binary') # ... text.force_encoding('UTF-8') 

或者,如果您无法更改获取数据的方式,则在尝试encode时将遇到Encoding::UndefinedConversionError 。 正如本博文中所详述的 ,解决方案是为五个未定义的CP1252字节指定编码:

 fallback = { "\u0081" => "\x81".force_encoding("CP1252"), "\u008D" => "\x8D".force_encoding("CP1252"), "\u008F" => "\x8F".force_encoding("CP1252"), "\u0090" => "\x90".force_encoding("CP1252"), "\u009D" => "\x9D".force_encoding("CP1252") } text.encode('CP1252', fallback: fallback).force_encoding('UTF-8')