【问题标题】:In Ruby, how to UTF-8 encode this weird character?在 Ruby 中,如何对这个奇怪的字符进行 UTF-8 编码?
【发布时间】:2015-05-28 03:48:50
【问题描述】:

我正在从感染了各种奇怪字符的外部数据库导入内容,例如

> str
=> "Nature’s Variety, Best Friends Animal Society team up"

从上下文看来, 代表一个正确的单引号。在cp1252编码中:

> str.encode('cp1252')
=> "Nature\xE2\x80\x99s Variety, Best Friends Animal Society team up"

那么如何将其转换为正确的 UTF-8 字符呢?这是我尝试过的:

> str.encode('UTF-8')
=> "Nature’s Variety, Best Friends Animal Society team up"

> str.encode('cp1252').encode('UTF-8')
=> "Nature’s Variety, Best Friends Animal Society team up"

> str.encode('UTF-8', invalid: :replace, replace: '?', undef: :replace)
=> "Nature’s Variety, Best Friends Animal Society team up"

> str.encode('cp1252').encode('UTF-8', invalid: :replace, replace: '?', undef: :replace)                                                                  
=> "Nature’s Variety, Best Friends Animal Society team up"

我宁愿找到一种方法来进行通用重新编码,以便它可以处理所有此类未编码的字符。但是,如果必须,我会进行个人搜索和替换。但我也无法做到这一点:

> str.encode('cp1252').gsub('\xE2/x80/x99', "'")
=> "Nature\xE2\x80\x99s Variety, Best Friends Animal Society team up"

> str.encode('cp1252').gsub(%r{\xE2\x80\x99}, "'")
SyntaxError: unexpected tIDENTIFIER, expecting $end

> str.encode('cp1252').gsub(Regexp.escape('\xE2\x80\x99'), "'")
=> "Nature\xE2\x80\x99s Variety, Best Friends Animal Society team up"

我想这样做,但我什至无法将这些字符粘贴到我的 REPL 中:

> str.gsub('’', "'")

当我尝试时,我得到:

> str.gsub('C"b,b,b
* "', ",")
=> "Nature’s Variety, Best Friends Animal Society team up"

令人沮丧。有关如何将其正确编码为 UTF-8 的任何建议?

编辑:根据字符串中实际字节的请求:

> str.bytes.to_a.join(' ')
=> "78 97 116 117 114 101 195 162 226 130 172 226 132 162 115 32 86 97 114 105 101 116 121 44 32 66 101 115 116 32 70 114 105 101 110 100 115 32 65 110 105 109 97 108 32 83 111 99 105 101 116 121 32 116 101 97 109 32 117 112"

【问题讨论】:

  • str.encoding 是什么’ 混乱?底层字节是什么?
  • @muistooshort, str.encoding # => #<Encoding UTF-8>

标签: ruby encoding utf-8


【解决方案1】:

一旦 Ruby 编码错误,字符将保持不正确,根据最初的错误。转换只是将现在错误的字符转换为新的编码。

要纠正 Ruby 的输入错误,您需要使用 force_encoding 方法,该方法不进行转换,它只是纠正了 Ruby 关于 String 编码的注释。

在您的情况下,故障发生在您从数据库中读取值之前。如果您挑选出问题字节:bytes = %w(195 162 226 130 172 226 132 162).map(&:to_i) 它们看起来是 UTF-8 编码,并且 已经 在数据库中进行了双重编码。您可能会假设将这些写入数据库的任何内容都存在问题(请注意,如果它是一个实时进程,这是一个需要排序的错误,您将继续获取这些错误值)。

发生的情况是您的数据库(或写入它的代码)收到了一些代表正确字符的 UTF-8 字节,但假设它们是 CP1252 以转换为 UTF-8。它进行了转换并将有效的 UTF-8(但错误的字符)写入数据库。

如果我在终端中使用 UTF-8 编码并作为默认 Ruby 编码在 Ruby 控制台中执行以下操作,我可以复制您的问题:

str = "Nature’s Variety, Best Friends Animal Society team up"
 => "Nature’s Variety, Best Friends Animal Society team up"
str = str.force_encoding('CP1252').encode('UTF-8')
 => "Nature’s Variety, Best Friends Animal Society team up"

故障是可逆的,如下图:

str = str.encode('CP1252').force_encoding('UTF-8')
 => "Nature’s Variety, Best Friends Animal Society team up"

encode('CP1252') 撤销原来的错误转换。

force_encoding('UTF-8') 将编码设置回系统最有可能收到的编码。

您将希望找到在您的系统中假设 CP1252 输入的位置,而不是假设 UTF-8(如果您有多个不同编码的源,它可能会变得更复杂)。

【讨论】:

  • 这对我来说失败了。该行产生 ArgumentError: wrong number of arguments(1 for 0)。将 encoding 更改为 encode 会产生一个带有其他怪异的字符串:““Natureâ€â€¢s Variety,Best Friends Animal Society 合作”
  • 对不起,我把第二个方法名弄错了,已更正。您的原始字符串不在 CP1252 编码中。你能在之前encode的任何调用提供原始字节吗?
  • 原始字节在问题的顶部:“Nature's Variety, Best Friends Animal Society team up”
  • @Mori:这些不是字节——它们是 Ruby 解释的字符,假设默认或输入编码是什么(你没有给出)。 str.bytes 将给出字节。我需要先查看字节,然后才能猜出数据库中的编码。您也可能在上游遇到了一些问题,并且数据库条目已经包含一些错误转换或双重编码的条目。
  • str.bytes 产生一个枚举器。我如何从中获取实际字节数?
【解决方案2】:

Fixing Incorrect String Encoding From MySQL 有这个问题。您需要设置正确的编码,然后强制返回。

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")
}

str.encode('CP1252', fallback: fallback).force_encoding('UTF-8')

根据您的数据,可能不需要后备,但它通过处理 CP1252 中未定义的五个字节来确保不会引发错误。

【讨论】:

  • 这很荒谬,这是解决这个问题的方法,但是效果很好,所以谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-02
  • 1970-01-01
  • 2015-03-28
  • 2021-08-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多