2012-04-05 3 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. Тем не менее, он выглядит как один символ, и это имеет значение при размещении дисплеев фиксированной ширины.

Например, посмотрите на две записи для «moiré.svg» на this directory listing и обратите внимание, как один из них перепутал выравнивание столбцов.

Как вычислить «моноширинную визуальную длину» строки в Ruby, которая не включает символы объединения нулевой ширины? (Один действительный метод может быть способ, чтобы преобразовать строку Unicode в ее каноническом представлении, перевернув выше в "\xC3\xA9", который также выглядит как é но имеет length из 1.)

+0

Какая версия Ruby у вас есть? Я попробовал ваш пример и получил '[3, 3," é "]'. –

+0

@IliaFrenkel Вышеупомянутое относится к Ruby 1.9 с кодировкой UTF-8 для строк. Я редактировал код, чтобы показать волшебный комментарий, который потребуется для автономного скрипта в любой системе, где UTF-8 не является значением по умолчанию. – Phrogz

ответ

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 –

+1

Или еще лучше. Существует 'display_width', который принимает строку, а не символ. –

-1

Я далек от того, чтобы быть экспертом в Ruby, но this дает следующее:

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

Это также дает '2' для строки, предоставленной @Phrogz. –

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 

Это работает в этом случае, но вы должны были бы какие свойства исключить в более надежном решении.

Использование жёсткого диска unicode_utils, как предложено в @joelparkerhenderson's answer, вероятно, будет лучшим вариантом, но я подумал, что включу это для полноты.

+0

Мне нравится этот ответ для его простоты и использования только основного Ruby. Будет ли 's.gsub (/ \ p {Mn} /, ''). Length' неправильно работает при некоторых обстоятельствах? – Phrogz

+0

@Phrogz, который, кажется, работает, и более лаконичен, чем мой. Я думаю, это зависит от того, как такие вещи, как 'gsub', взаимодействуют с Unicode, комбинируя метки, например. является ли нынешнее поведение просто случайным или преднамеренным, и как оно может измениться в будущем. Я думаю, что мораль - это убедиться, что у вас есть тесты на месте. – matt

Смежные вопросы