如何检查字符是否为UTF-8

13
如何通过 Ruby 或 Rails 检查字符集是否使用 UTF-8 编码?

你的意思是如果你已经有了正确编码的字符串,还是指在你甚至开始将字符串读入内存之前(比如从磁盘上的文件中)? - d11wtq
3个回答

20

检查 UTF-8 的有效性

对于大多数多字节编码,可以通过编程方式检测无效的字节序列。由于 Ruby 默认将所有字符串视为 UTF-8,因此您可以检查字符串是否是有效的 UTF-8

# encoding: UTF-8
# -------------------------------------------
str = "Partly valid\xE4 UTF-8 encoding: äöüß"

str.valid_encoding?
   # => false

str.scrub('').valid_encoding?
   # => true

转换编码

如果一个字符串不是有效的 UTF-8 编码,但你知道实际的字符编码,你可以将该字符串转换为 UTF-8 编码。

示例
有时候,您会遇到这样的情况:您知道输入文件的编码是 UTF-8 或者 CP1252 (也称为 Windows-1252)之一。
检查它的编码并将其转换为 UTF-8(如果需要):

# encoding: UTF-8
# ------------------------------------------------------
test = "String in CP1252 encoding: \xE4\xF6\xFC\xDF"
File.open( 'input_file', 'w' ) {|f| f.write(test)}

str  = File.read( 'input_file' )

unless str.valid_encoding?
  str.encode!( 'UTF-8', 'CP1252', invalid: :replace, undef: :replace, replace: '?' )
end #unless
   # => "String CP1252 encoding: äöüß"

=======
注释

  • 通过编程,可以以相当高的可靠性检测大多数多字节编码,如UTF-8(在Ruby中,请参考:#valid_encoding?)。仅经过16个字节后,随机的字节序列成为有效UTF-8的概率仅为0.01%。(与依赖于UTF-8 BOM进行比较)

  • 然而,编程检测(无效)单字节编码,如CP1252ISO-8859-1并不容易。因此,上述代码片段不能反向工作,即无法检测 CP1252 编码字符串是否有效。

  • 尽管UTF-8已成为Web中默认编码越来越受欢迎,但 CP1252 和其他 Latin1 变体仍然在西方国家特别是北美非常流行。请注意,还有一些非常相似但略有不同于 CP1252 (也称为 Windows-1252 )的单字节编码。例如: ISO-8859-1 ISO-8859-15


"CP1252 编码的字符串: \xE4\xF6\xFC\xDF" 我相信问题是如何检查它是否为 CP1252 编码。 - Mladen Jablanović
我认为问题是给定的字符串是否采用(有效的)UTF-8编码。您可以通过在采用UTF-8编码的字符串str上调用str.valid_encoding?来实现。我的回答没有表达清楚吗? - Andreas Rayo Kniep
从程序上讲,你不能(或者至少不容易且当然不可靠地)检查像CP1252这样的单字节编码字符串的无效性。但是,你可以相当可靠地(取决于字符串的长度)检查多字节编码字符串(如UTF-8)的无效性。 - Andreas Rayo Kniep

13

在 Ruby 或任何地方,没有确切的方法来做到这一点:

str = 'foo' # start with a simple string
# => "foo" 
str.encoding
# => #<Encoding:UTF-8> # which is UTF-8 encoded
str.bytes.to_a
# => [102, 111, 111] # as you can see, it consists of three bytes 102, 111 and 111
str.encode!('us-ascii') # now we will recode the string to 8-bit us-ascii encoding
# => "foo" 
str.encoding
# => #<Encoding:US-ASCII> 
str.bytes.to_a
# => [102, 111, 111] # see, same three bytes
str.encode!('windows-1251') # let us try some cyrillic
# => "foo" 
str.encoding
# => #<Encoding:Windows-1251> 
str.bytes.to_a
# => [102, 111, 111] # see, the same three again!
当然,你可以对文本进行一些统计分析,并消除文本无效的编码,但从理论上讲,这是一个不可解决的问题。

“在Ruby或其他任何地方,都没有确定的方法来做到这一点。”就像一个经历过Unicode战争的疲惫士兵所说的那样。我曾经历过这种情况,感同身受。 :-) 我也退而求其次,采用文本的统计分析方法,这种方法有时候有效。令人惊讶的是,当有人不顾规范而努力使事情正常运行时,HTML、RSS和XML可能会出现多么严重的故障。 - the Tin Man
2
String#valid_encoding? 是什么意思?例如:"部分有效的\xE4 UTF-8 编码: äöüß".valid_encoding? - Andreas Rayo Kniep
valid_encoding? 检查字符串是否包含无效的字节序列。它并不表示该(本来有效的)字节序列来自于某种编码,我认为这就是问题所在。 - Mladen Jablanović
好的,我明白了。但是我理解的问题不同。我的理解是:“如何检查给定的字符串是否为有效的UTF-8编码?” 你可以非常可靠地确定给定的字节序列是否为有效的UTF-8:仅仅16个字节后,随机字节序列被确认为有效的UTF-8的概率只有0.01%。 因此,str.valid_encoding?算法在确定给定的字符串是否为UTF-8编码方面是相当可靠的。 - Andreas Rayo Kniep

1
"your string".encoding
 # => #<Encoding:UTF-8>

或者如果你想要以编程方式实现,

"your string".encoding.name == "UTF-8"
 # => true

9
这只是检查字符串对象上设置的编码方式,而不是其内容实际使用的编码方式。不能保证实际内容使用相同的编码方式进行编码。 - Mladen Jablanović

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接