Ruby,比较字符串与UTF-8字符的问题

时间:2015-11-24 14:52:35

标签: ruby ruby-on-rails-3 unicode utf-8 character-encoding

我有这两个UTF-8字符串:

a = "N\u01b0\u0303"
b = "N\u1eef"

它们看起来很不一样,但渲染后它们是相同的:

irb(main):039:0> puts "#{a} - #{b}"
Nữ - Nữ

a 版本是我存储在数据库中的版本。 b 版本是一个来自浏览器的POST请求,我不知道为什么浏览器会发送不同的UTF8字符组合,而且它总是不会发生,我可以'在我的开发环境中重现这个问题,它发生在生产中,占总请求的百分比。

案例是我尝试比较它们,但它们返回false

irb(main):035:0> a == b
=> false

我尝试了不同的东西,例如强制编码

irb(main):022:0> c.force_encoding("UTF-8") == a.force_encoding("UTF-8")
=> false

另一个有趣的事实是:

irb(main):005:0> a.chars
=> ["N", "ư", "̃"]
irb(main):006:0> b.chars
=> ["N", "ữ"]

如何比较这些字符串?

2 个答案:

答案 0 :(得分:9)

这是Unicode equivalence的问题。

字符串的a版本包含字符ư(U + 01B0:LATIN SMALL LETTER U WITH HORN),后跟U + 0303 COMBINING TILDE。第二个字符,顾名思义是combining character,在渲染时与前一个字符组合以产生最终字形。

字符串的b版本使用字符(U + 1EEF,LATIN SMALL LETTER U WITH HORN和TILDE),这是一个单个字符,并且等效到前一个组合,但使用不同的字节序列来表示它。

为了比较这些字符串,您需要规范化它们,以便它们对这些类型的字符使用相同的字节序列。当前版本的Ruby内置了这个版本(在早期版本中,您需要使用第三方库)。

所以目前你有

a == b

false,但如果你这样做

a.unicode_normalize == b.unicode_normalize

你应该得到true

如果您使用的是旧版本的Ruby,则有几种选择。 Rails有一个normalize方法作为其多字节支持的一部分,所以如果你使用Rails,你可以这样做:

a.mb_chars.normalize == b.mb_chars.normalize

或者类似的东西:

ActiveSupport::Multibyte::Unicode.normalize(a) == ActiveSupport::Multibyte::Unicode.normalize(b)

如果您没有使用Rails,那么您可以查看unicode_utils gem,并执行以下操作:

UnicodeUtils.nfkc(a) == UnicodeUtils.nfkc(b)

nfkc指的是规范化形式,它与其他技术中的默认形式相同。)

有各种不同的方法来规范化unicode字符串(即你是使用分解版本还是组合版本),这个例子只使用默认值。我会留下研究差异给你。

答案 1 :(得分:3)

您可以看到这些是不同的字符。 Firstsecond。在第一种情况下,它使用修饰符“combining tilde”。

维基百科有一节关于此:

  

在打印或显示时,假定定义为规范等效的代码点序列具有相同的外观和含义。例如,代码点U + 006E(拉丁文小写“n”)后跟U + 0303(组合波形符“◌”)由Unicode定义为规范等效于单个代码点U + 00F1(小写)字母“ñ”的西班牙字母表)。因此,这些序列应以相同的方式显示,应按照字母顺序名称或搜索等应用程序进行相同处理,并可互相替换。

  

该标准还定义了一个文本规范化过程,称为Unicode规范化,它取代了等效的字符序列,以便任何两个等价的文本将被简化为相同的代码点序列,称为规范化形式或正常形式的原文。

Ruby似乎支持这种规范化,但是only as of Ruby 2.2

http://ruby-doc.org/stdlib-2.2.0/libdoc/unicode_normalize/rdoc/String.html

a = "N\u01b0\u0303".unicode_normalize
b = "N\u1eef".unicode_normalize

a == b  # true

或者,如果您使用Ruby on Rails,似乎有built-in method用于规范化。