2012-04-05 117 views
4

考虑以下Ruby代码分析一个三字节的UTF-8字符串:计数Unicode字符串长度而不组合标记

#encoding: utf-8 
s = "\x65\xCC\x81" 
p [s.bytesize, s.length, s, s.encoding.name] 
#=> [3, 2, "é", "UTF-8"] 

如上所述on this page of mine上述真的两个字符的字符串:拉丁小写e其次是Combining Acute Accent。然而,它看起来像一个字符,这在布置固定宽度显示器时很重要。

例如,看一下上this directory listing两个条目为“moiré.svg”并注意其中一人搅乱了列对齐。

如何计算Ruby中字符串的“等宽视觉长度”,该字符串不包含任何零宽度组合字符? (一个有效的技术可能转化为Unicode字符串转换成其规范的表达方式,转向到上述"\xC3\xA9"这也像é但有1一个length。)

+0

你有哪个版本的Ruby?我尝试了你的例子,并得到了[3,3,“é”]'。 – 2012-04-05 01:46:19

+0

@IliaFrenkel上面提到了Ruby 1.9,其字符串的编码为UTF-8。我编辑了代码,以显示UTF-8不是默认的任何系统上的独立脚本所需的魔法注释。 – Phrogz 2012-04-05 01:47:30

回答

5

的unicode_utils宝石可以帮助:

http://unicode-utils.rubyforge.org/UnicodeUtils.html

有一个char_display_width方法:

require "unicode_utils/char_display_width" 
UnicodeUtils.char_display_width("別") # => 2 
UnicodeUtils.char_display_width(0x308) # => 0 
UnicodeUtils.char_display_width("a") # => 1 

有一个字符串display_width方法:

require "unicode_utils/display_width" 
UnicodeUtils.display_width("別れ") => 4 
UnicodeUtils.display_width("12") => 2 
UnicodeUtils.display_width("a\u{308}") => 1 

也期待在each_grapheme

(感谢迈克尔·安德森指出的其他方法)

+0

刚发现这个我自己..但我认为使用'each_grapheme'方法计数可能更合适。 http://unicode-utils.rubyforge.org/UnicodeUtils.html#method-c-each_grapheme – 2012-04-05 02:14:33

+1

或更好。有一个'display_width'接受一个字符串而不是一个字符。 – 2012-04-05 02:16:45

-1

我远远不是Ruby的专家,但this给出了以下情况:

def length_utf8 
    count = 0 
    scan(/./mu) { count += 1 } 
    count 
end 
+2

这也给了@Phrogz提供的字符串'2'。 – 2012-04-05 02:05:24

1

你可以使用正则表达式来获得在Unicode属性:

s = "\x65\xCC\x81" 
count = s.each_char.inject(0) do |c, char| 
    c += 1 unless char=~/\p{Mn}/ 
    c 
end 

puts count #=> 1 

这个工程在这种情况下,但你必须计算出在更强大的解决方案中排除哪些属性。

使用@joelparkerhenderson's answer中建议的unicode_utils gem可能是一个更好的选择,但我想我会包括这个完整性。

+0

我喜欢这个答案,因为它的简单性和只使用核心Ruby。在某些情况下''.gsub(/ \ p {Mn} /,'').length'不能正确工作吗? – Phrogz 2012-04-05 02:33:28

+0

@Phrogz似乎工作,并比我更简洁。我想这取决于'gsub'如何与Unicode组合标记交互,例如目前的行为是否仅仅是一种意外,或者是否是故意的,以及未来如何改变。我想道德是确保你有测试。 – matt 2012-04-05 02:45:42

相关问题