Ruby,将字符串与 UTF-8 字符进行比较时出现问题

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

我有这 2 个 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", "ữ"]

如何比较这些字符串?

最佳答案

这是 Unicode equivalence 的问题.

字符串的 a 版本由字符 ư(U+01B0:带有喇叭的拉丁文小写字母 U)和后面的 U+0303 COMBINING TILDE 组成。顾名思义,第二个字符是 combining character ,在呈现时与前一个字符组合以生成最终字形。

字符串的 b 版本使用字符 (U+1EEF,带有喇叭和波浪线的拉丁文小写字母 U),它是单个字符,并且是 < em>等价于前面的组合,但使用不同的字节序列来表示它。

为了比较这些字符串,您需要对它们进行规范化,以便它们都对这些类型的字符使用相同的字节序列。当前版本的 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 字符串有多种不同的方法(即使用分解版本还是组合版本),本示例仅使用默认值。我会把研究差异留给你。

关于Ruby,将字符串与 UTF-8 字符进行比较时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33896771/

相关文章:

ruby-on-rails - Rubymine 弃用警告

java - 以编程方式将 Unicode 转换为转义的 Unicode

java - 我如何从android中的应用程序发送阿拉伯语单词并在阿拉伯语的mysql数据库上恢复?

c - 将 "\\?\"字符串添加到路径 - DriverPackageUninstall

python - 协助 ruby​​ def

ruby-on-rails - 是否可以使 Ohm for Ruby 中的整个对象的内容过期?

ruby-on-rails - 无法在 Rails 4 中检索 attr_accessor

ruby-on-rails - 工厂女郎 - 有很多联想

ruby-on-rails - 为什么这在我的 Rails Controller 中不起作用

javascript - kaminari 通过 ajax 分页,remote = true 不影响 rails 3 中的 View