Ruby,比较字符串和UTF-8字符时出现问题

Ruby,比较字符串和UTF-8字符时出现问题,ruby,ruby-on-rails-3,unicode,utf-8,character-encoding,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版本是我存储在DB中的版本。b版本是在POST请求中来自浏览器的版本,我不知道为什么浏览器会发送不同的UTF8字符组合,而且这种情况并不总是发生,我无法在我的开发环境中重现该问题,它发生在生产中,占总请求的百分比 情况是,我尝试比较这两种方法,但它们返回fal

我有两个UTF-8字符串:

a = "N\u01b0\u0303"
b = "N\u1eef"
它们看起来非常不同,但渲染后的效果相同:

irb(main):039:0> puts "#{a} - #{b}"
Nữ - Nữ
a版本是我存储在DB中的版本。b版本是在POST请求中来自浏览器的版本,我不知道为什么浏览器会发送不同的UTF8字符组合,而且这种情况并不总是发生,我无法在我的开发环境中重现该问题,它发生在生产中,占总请求的百分比

情况是,我尝试比较这两种方法,但它们返回
false

irb(main):035:0> a == b
=> false
我尝试过不同的方法,比如强制编码:

另一个有趣的事实是:

irb(main):005:0> a.chars
=> ["N", "ư", "̃"]
irb(main):006:0> b.chars
=> ["N", "ữ"]
如何比较这类字符串?

这是一个问题

字符串的
a
版本由字符
ư
(U+01B0:带喇叭的拉丁文小写字母U)组成,后跟U+0303组合波浪号。顾名思义,第二个字符是一个,渲染时与前一个字符组合以生成最终的glyph

字符串的
b
版本使用字符
(U+1EEF,带喇叭和波浪号的拉丁小写字母U),是单个字符,与前面的组合等效,但使用不同的字节序列表示

为了比较这些字符串,您需要对它们进行规范化,以便它们对这些类型的字符都使用相同的字节序列。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,那么您可以查看,并执行以下操作:

UnicodeUtils.nfkc(a) == UnicodeUtils.nfkc(b)
nfkc
指规范化形式,与其他技术中的默认形式相同。)


标准化unicode字符串有多种不同的方法(即使用分解版本还是组合版本),本例仅使用默认值。我将把研究差异留给你。

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

Wikipedia有一个关于这方面的章节:

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

该标准还定义了一个称为Unicode规范化的文本规范化过程,该过程将替换等效的字符序列,以便将任何两个等效的文本缩减为相同的代码点序列,称为原始文本的规范化形式或规范化形式

Ruby似乎支持这种规范化,但是:


或者,如果您使用的是RubyonRails,那么规范化方面似乎有一个解决方案。

您是否从同一个浏览器和操作系统中获得了a和b?在我看来,可能是特定的浏览器/os角色渲染问题。也许你们可以尝试点替换表,然后进行反向替换。我使用的是Ruby 2.0.0p247,它看起来并没有集成这个模块。有推荐的第三方库吗?我在Github上找到了但没有任何开始,而且我在安装它时也遇到了问题。@fguillen我已经用一些建议更新了答案。你的问题是用Rails标记的,所以我认为使用Rails的支持可能是最好的解决方案。你是对的,我在Rails的内部Unicode模块中没有想到。我已经在您的答案中添加了此escenario的示例,如果不正确,请更正。
UnicodeUtils.nfkc(a) == UnicodeUtils.nfkc(b)
a = "N\u01b0\u0303".unicode_normalize
b = "N\u1eef".unicode_normalize

a == b  # true