2015-10-26 17 views
9

Jeśli mam ciąg znaków Unicode w języku Python zawierający kombinację znaków, len zgłasza wartość, która nie odpowiada liczbie znaków "widzianych".Jak uzyskać "widoczną" długość łączenia ciągu znaków Unicode w języku Python?

Na przykład, jeśli mam ciąg znaków zawierający kombinacje i podkreślenia, takie jak u'A\u0332\u0305BC', len(u'A\u0332\u0305BC'), raporty 5; ale wyświetlany ciąg ma tylko 3 znaki.

Jak uzyskać "widoczny" - czyli liczbę różnych pozycji zajmowanych przez ciąg znaków widziany przez użytkownika - długość ciągów Unicode zawierających kombinację glifów w języku Python?

+0

hmm to jest interesujące, najlepsze, jakie mam Myślę, że to po prostu rozbierać niechciane znaki. – postelrich

+0

@riotburn: To będzie trudne. Znaki mogą być dowolne (dostarczone przez użytkownika). Musiałbym sprawdzić listę tego, co łączą glify Unicode - chyba że jest to systemowa część kodowania. – orome

Odpowiedz

4

Funkcja unicodedata module ma funkcję combining, której można użyć do określenia, czy pojedynczy znak jest znakiem łączącym. Jeśli zwróci 0, możesz policzyć znak jako nie łączący.

import unicodedata 
len(u''.join(ch for ch in u'A\u0332\u0305BC' if unicodedata.combining(ch) == 0)) 

lub nieco prostsze:

sum(1 for ch in u'A\u0332\u0305BC' if unicodedata.combining(ch) == 0) 
+1

Lub: 'sum (not unicodedata.combining (ch) dla ch in u \ u0332 \ u0305BC ')'. – Bakuriu

+0

@ Bakuriu na początku Myślałem, że to nie zadziała, ponieważ 'łączenie' zwraca liczby całkowite, które nie są' 0' lub '1', ale' not' dba o to. Dobra robota! –

+2

Nie działa to w przypadku klastrów grafem wykonanych ze znaków niebędących znakami, na przykład: "u" \ u1161 \ u11A8 "(각). –

4

Jeśli masz smak regex który obsługuje dopasowywanie grapheme, można użyć \X

Demo

Chociaż moduł domyślny Python re robi nie obsługuje \X, Matthew Barnett's regex module ma:

>>> len(regex.findall(r'\X', u'A\u0332\u0305BC')) 
3 

W Pythonie 2, trzeba użyć u we wzorcu:

>>> regex.findall(u'\\X', u'A\u0332\u0305BC') 
[u'A\u0332\u0305', u'B', u'C'] 
>>> len(regex.findall(u'\\X', u'A\u0332\u0305BC')) 
3 
2

Łącząc znaki nie są jedynymi znaków o zerowej szerokości:

>>> sum(1 for ch in u'\u200c' if unicodedata.combining(ch) == 0) 
1 

("\u200c" lub "‌" jest zero-joiner; jest to znak niedrukujące)

W tym przypadku moduł regex nie działa albo.

>>> len(regex.findall(r'\X', u'\u200c')) 
1 

znalazłem wcwidth który obsługuje powyższy przypadek poprawnie:

>>> from wcwidth import wcswidth 
>>> wcswidth(u'A\u0332\u0305BC') 
3 
>>> wcswidth(u'\u200c') 
0 

ale nadal nie robi Wygląda na to, że działa na przykładzie użytkownika 596219:

>>> wcswidth('각') 
4 
Powiązane problemy