处理ruby中的编码

时间:2015-08-14 07:31:41

标签: ruby encoding utf-8

我有一个好字符串和一个坏字符串

处理我做的坏字符串

bad.encode("iso-8859-1").force_encoding("utf-8")

使其可读

如果我这样做

good.encode("iso-8859-1").force_encoding("utf-8")

我得到Encoding::UndefinedConversionError: U+05E2 from UTF-8 to ISO-8859-1

好的和坏的字符串在开头都是UTF-8,但是良好的字符串是可读的,坏的是坏的。

我不知道如何检测字符串是否良好,我正在尝试找到一种方法来处理字符串并使其以正确的编码方式可读

类似的东西

if needs_fixin?(str)
  str.encode("iso-8859-1").force_encoding("utf-8")
else
  str
end

我唯一能想到的是捕获异常跳过编码修复部分,但我不希望代码故意出现异常。

类似于str.try(:encode, "iso-8859-1").force_encoding("utf-8") rescue str

坏字符串就像是

×¢×××× ×¢×¥ ×'××¤×¡× ×פת×ר ×× ××רק××

1 个答案:

答案 0 :(得分:4)

我怀疑你的问题是双重编码的字符串。由于各种原因,这是非常糟糕的,但是这里的tl; dr是不完全可修复的,你应该修改字符串的根本问题,如果可能的话,双重编码。

这会产生一个带有UTF-8字符的双重编码字符串:

> str = "汉语 / 漢語"
 => "汉语 / 漢語"
> str.force_encoding("iso-8859-1")
 => "\xE6\xB1\x89\xE8\xAF\xAD / \xE6\xBC\xA2\xE8\xAA\x9E"
> bad = str.force_encoding("iso-8859-1").encode("utf-8")
 => "æ±\u0089语 / æ¼¢èª\u009E"

然后你可以通过将双重编码的UTF-8重新解释为ISO-8859-1然后声明编码实际上是UTF-8来修复它

> bad.encode("iso-8859-1").force_encoding("utf-8")
 => "汉语 / 漢語"

但是您无法将实际的UTF-8字符串转换为ISO-8859-1,因为UTF-8中的代码点ISO-8859-1没有任何明确的编码方法< / p>

> str.encode("iso-8859-1")
Encoding::UndefinedConversionError: ""\xE6\xB1\x89"" from UTF-8 to ISO-8859-1

现在,您无法始终检测并修复此问题,因为"there's no way to tell whether the result is from incorrectly double-encoding one character, or correctly single-encoding 2 characters."

所以,你留下的最好的是启发式。 Borshuno的建议在这里不起作用,因为它实际上会破坏不可转换的字节:

> str.encode( "iso-8859-1", fallback: lambda{|c| c.force_encoding("utf-8")} )
 .0=> " / "

如果可能的话,最好的做法是修复双重编码问题,以便它根本不会发生。如果您怀疑它们可能被双重编码,那么下一个最佳操作方法是将BOM字节添加到您的UTF-8字符串,因为您可以检查这些字节并确定您的字符串是否已被重新编码。

> str_bom = "\xEF\xBB\xBF" + str
 => "汉语 / 漢語"
> str_bom.start_with?("\xEF\xBB\xBF")
 => true
> str_bom.force_encoding("iso-8859-1").encode("utf-8").start_with?("\xEF\xBB\xBF")
 => false

如果您可以假设BOM在您的&#34;正确的&#34;字符串,然后您可以通过检查BOM是否存在来检查双重编码。如果不是(即它已被重新编码),那么您可以执行解码程序:

> str_bom.force_encoding("iso-8859-1").encode("utf-8").encode("iso-8859-1").force_encoding("utf-8").start_with?("\xEF\xBB\xBF")
 => true

如果您无法确定BOM,那么您可以使用启发式方法来猜测字符串是否是&#34;坏&#34;是否通过计算不可打印的字符,或超出正常预期结果集的字符(你的字符串看起来像是处理希伯来语;你可以说任何由> 50%非希伯来字母组成的字符串例如,是双重编码的,所以你可以尝试解码它。

最后,您将不得不回退到异常处理,并希望您知道字符串在双重编码时声称为哪个编码声明:

str = "汉语 / 漢語"
begin
  str.encode("iso-8859-1").encode("utf-8")
rescue Encoding::UndefinedConversionError
  str
end

但是,即使你知道一个字符串是双重编码的,如果你不知道它被不正确地声明为转换为UTF-8时的编码,你就不能这样做逆向操作:

> bad_str = str.force_encoding("windows-1252").encode("utf-8")
 => "汉语 / 漢語"
> bad_str.encode("iso-8859-1").force_encoding("utf-8")
Encoding::UndefinedConversionError: "\xE2\x80\xB0" from UTF-8 to ISO-8859-1

由于字符串本身不会携带有关编码错误的任何信息,因此您没有足够的信息可靠地解决问题,而是通过迭代最可能的列表使用希伯来语启发式编码和启发式检查每次成功重新编码的结果。

回应我链接的帖子:字符编码很难。