2012-04-05 60 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