在红宝石和其他编程语言中,西里尔字符串Я̆Я̄Я̈返回长度2而不是1

tom*_*ral 6 ruby string ruby-on-rails utf-8 unicode-normalization

在Ruby,Javascript和Java(其他我没试过)中,有西里尔字符Я̆Я̄Я̈长度2.当我尝试用这些字符检查字符串的长度时,我的输出值很差.

"??".mb_chars.length
#=> 2  #should be 1 (ruby on rails)

"??".length
#=> 2  #should be 1 (ruby, javascript)

"?".length
#=> 1  #correct (ruby, javascript)
Run Code Online (Sandbox Code Playgroud)

请注意,字符串以UTF-8编码,每个字符串都表现为单个字符.

我的问题是为什么会有这样的行为,如何在这些字符内正确获取字符串的长度?

mu *_*ort 5

根本问题是??实际上是两个代码点:?和变音符号是分开的:

'??'.chars
#=> ["?", "?"]
Run Code Online (Sandbox Code Playgroud)

通常你会通过unicode规范化来解决这类问题,但由于没有单一的代码点????(但有),这一点对你没有帮助?.

你可以在检查长度之前剥掉变音符号:

'??'.gsub(/\p{Diacritic}/, '')
#=> "?" 
'??'.gsub(/\p{Diacritic}/, '').length
#=> 1 
Run Code Online (Sandbox Code Playgroud)

你会得到所需的长度但字符串不会完全相同.这也适用于?可由单个代码点表示的事物:

'?'.length
#=> 1
'?'.gsub(/\p{Diacritic}/, '')
#=> "?" 
'?'.gsub(/\p{Diacritic}/, '').length
#=> 1 
Run Code Online (Sandbox Code Playgroud)

Unicode非常精彩且令人敬畏,解决了许多困扰我们的问题.不幸的是,Unicode也是可怕和复杂的,因为人类语言和字形并没有完全设计.


Ste*_*fan 5

Ruby 2.5补充说String#each_grapheme_cluster:

'??????'.each_grapheme_cluster.to_a   #=> ["??", "??", "??"]
'??????'.each_grapheme_cluster.count  #=> 3
Run Code Online (Sandbox Code Playgroud)

请注意,您不能使用each_grapheme_cluster.size等效的each_char.size,因此6在上面的示例中都会返回.(这看起来像一个错误,我刚刚提交了一份错误报告)