2012-04-05 7 views
4

Betrachten Sie den folgenden Ruby-Code ein Drei-Byte-UTF-8-String Analyse:Zählen Unicode-String Länge ohne Kombinationszeichen

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

Wie beschrieben on this page of mine die oben wirklich ist ein zweistelliges String: Latin Klein e gefolgt von Combining Acute Accent. Jedoch sieht es wie ein Zeichen aus, und das ist wichtig beim Auslegen von Displays mit fester Breite.

Betrachten Sie zum Beispiel die zwei Einträge für "moiré.svg" unter this directory listing und beachten Sie, wie einer von ihnen die Spaltenausrichtung durcheinander gebracht hat.

Wie kann ich die 'sichtbare Monospace-Länge' eines Strings in Ruby berechnen, der keine Null-Breite-Kombinationszeichen enthält? (Eine gültige Technik könnte ein Weg sein, einen Unicode-Zeichenfolge in seine kanonischen Darstellung zu transformieren, die oben in "\xC3\xA9" drehen, die wie é sehen auch, aber ein length von 1.)

+0

Welche Version von Ruby hast du? Ich habe dein Beispiel ausprobiert und habe '[3, 3," é "]'. –

+0

@IliaFrenkel Das obige bezieht sich auf Ruby 1.9 mit einer Kodierung von UTF-8 für Strings. Ich habe den Code bearbeitet, um den magischen Kommentar anzuzeigen, der für ein Standalone-Skript auf jedem System erforderlich wäre, auf dem UTF-8 nicht der Standard ist. – Phrogz

Antwort

5

die unicode_utils gem helfen können:

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

Es gibt eine char_display_width Methode:

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

Es ist ein String display_width Methode:

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

Auch bei each_grapheme aussehen.

(Danke Michael Anderson für den Hinweis, die zusätzliche Methoden out)

+0

Ich habe das gerade selbst gefunden. Aber ich denke, das Zählen mit der 'each_grapheme'-Methode ist vielleicht angemessener. http://unicode-utils.rubyforge.org/UnicodeUtils.html#method-c-each_grapheme –

+1

Oder noch besser. Es gibt eine 'display_width', die eine Zeichenfolge anstelle eines Zeichens akzeptiert. –

-1

Ich bin weit davon entfernt ein Experte in Ruby zu sein, aber this gibt die folgende:

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

Dies gibt auch "2" für die von @Phrogz bereitgestellte Zeichenfolge. –

1

Sie einen regulären Ausdruck auf den Unicode-Eigenschaften erhalten verwenden:

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

puts count #=> 1 

Dies funktioniert in diesem Fall, aber man müßte herauszufinden, welche Eigenschaften in einer robusteren Lösung auszuschließen sind.

Die Verwendung des unicode_utils-Edelsteins, wie in @joelparkerhenderson's answer vorgeschlagen, wird wahrscheinlich eine bessere Option sein, aber ich dachte, ich würde dies zur Vollständigkeit hinzufügen.

+0

Ich mag diese Antwort für ihre Einfachheit und Verwendung nur Kern Ruby. Würde 's.gsub (/ \ p {Mn} /, ''). Length 'unter bestimmten Umständen nicht korrekt funktionieren? – Phrogz

+0

@Phrogz das scheint zu funktionieren, und ist prägnanter als meins. Ich denke, es hängt davon ab, wie Dinge wie "gsub" mit Unicode-Kombinationszeichen interagieren, z. ob das gegenwärtige Verhalten nur ein Unfall ist oder ob es beabsichtigt ist und wie es sich in der Zukunft ändern könnte. Ich denke, die Moral ist, dass Sie Tests an Ort und Stelle haben. – matt

Verwandte Themen