fgu*_*len 6 ruby unicode utf-8 character-encoding ruby-on-rails-3
我有这两个UTF-8字符串:
a = "N\u01b0\u0303"
b = "N\u1eef"
Run Code Online (Sandbox Code Playgroud)
它们看起来很不一样,但渲染后它们是相同的:
irb(main):039:0> puts "#{a} - #{b}"
N?? - N?
Run Code Online (Sandbox Code Playgroud)
的一个版本是我已经存储在DB的一个.该b版本是一个由在POST请求的浏览器来了,我不知道为什么浏览器发送的字符UTF8的不同组合,它并不总是发生,我无法重现该问题在我的dev环境,它发生在生产中,占总请求的百分比.
情况是我尝试比较它们,但它们返回false:
irb(main):035:0> a == b
=> false
Run Code Online (Sandbox Code Playgroud)
我尝试过强制编码等不同的东西:
irb(main):022:0> c.force_encoding("UTF-8") == a.force_encoding("UTF-8")
=> false
Run Code Online (Sandbox Code Playgroud)
另一个有趣的事实是:
irb(main):005:0> a.chars
=> ["N", "?", "?"]
irb(main):006:0> b.chars
=> ["N", "?"]
Run Code Online (Sandbox Code Playgroud)
我该如何比较这些字符串?
mat*_*att 10
这是Unicode等价的问题.
a你的字符串的版本包括字符?(U + 01B0:LATIN SMALL LETTER U WITH HORN),然后是U + 0303 COMBINING TILDE.顾名思义,这第二个字符是一个组合字符,在渲染时与前一个字符组合以产生最终字形.
b字符串的版本使用字符?(U + 1EEF,LATIN SMALL LETTER U WITH HORN和TILDE),它是单个字符,并且等同于之前的组合,但使用不同的字节序列来表示它.
为了比较这些字符串,您需要对它们进行标准化,以便它们对这些类型的字符使用相同的字节序列.当前版本的Ruby内置了此功能(在早期版本中,您需要使用第三方库).
所以目前你有
a == b
Run Code Online (Sandbox Code Playgroud)
这是false,但如果你这样做
a.unicode_normalize == b.unicode_normalize
Run Code Online (Sandbox Code Playgroud)
你应该得到true.
如果您使用的是旧版本的Ruby,则有几个选项.Rails有一个normalize方法作为其多字节支持的一部分,所以如果你使用Rails,你可以这样做:
a.mb_chars.normalize == b.mb_chars.normalize
Run Code Online (Sandbox Code Playgroud)
或者类似的东西:
ActiveSupport::Multibyte::Unicode.normalize(a) == ActiveSupport::Multibyte::Unicode.normalize(b)
Run Code Online (Sandbox Code Playgroud)
如果您没有使用Rails,那么您可以查看unicode_utils gem,并执行以下操作:
UnicodeUtils.nfkc(a) == UnicodeUtils.nfkc(b)
Run Code Online (Sandbox Code Playgroud)
(nfkc指标准化形式,与其他技术中的默认形式相同.)
有各种不同的方法来规范化unicode字符串(即你是使用分解版本还是组合版本),这个例子只使用默认值.我会留下研究差异给你.
| 归档时间: |
|
| 查看次数: |
1701 次 |
| 最近记录: |