Po pierwsze, w Python2, należy używać łańcuchów Unicode (u'<...>'
), aby znaki Unicode były postrzegane jako znaki Unicode. I correct source encoding, jeśli chcesz użyć samych znaków zamiast reprezentacji \UXXXXXXXX
w kodzie źródłowym.
Teraz, jak i za Python: getting correct string length when it contains surrogate pairsPython returns length of 2 for single Unicode character string w python2 „wąski” buduje (z sys.maxunicode==65535
), 32-bitowych znaków Unicode są reprezentowane surrogate pairs, a to nie jest przezroczysty dla funkcji łańcuchowych. Zostało to naprawione tylko w 3.3 (PEP0393).
Najprostsza rozdzielczość (z wyjątkiem migracji do wersji 3.3+) polega na kompilacji "szerokiej" kompilacji w języku Python ze źródła, jak opisano w trzecim linku. W nim znaki Unicode są 4-bajtowe (a więc są potencjalnym trybem pamięci), ale jeśli potrzebujesz rutynowo obsługiwać szerokie znaki Unicode, jest to prawdopodobnie akceptowalna cena.
Rozwiązaniem dla „wąski” budować jest aby niestandardowy zestaw funkcji ciągów (len
, slice
, może jako podklasa unicode
), które wykrywa zastępczych par i obsługiwać je za pomocą pojedynczego znaku.Nie mogłem łatwo znaleźć istniejący (co jest dziwne), ale nie jest to zbyt trudne do napisania:
- zgodnie UTF-16#U+10000 to U+10FFFF - Wikipedia,
- 1st charakter (wysoki zastępczym) jest w zasięgu
0xD800..0xDBFF
- 2. charakter (niska zastępczym) - w zakresie
0xDC00..0xDFFF
- te zakresy są zastrzeżone, a tym samym nie może występować jako zwykłe znaki
Więc oto kod do wykrycia zastępczego parę:
def is_surrogate(s,i):
if 0xD800 <= ord(s[i]) <= 0xDBFF:
try:
l = s[i+1]
except IndexError:
return False
if 0xDC00 <= ord(l) <= 0xDFFF:
return True
else:
raise ValueError("Illegal UTF-16 sequence: %r" % s[i:i+2])
else:
return False
i funkcję, która zwraca prosty kawałek:
def slice(s,start,end):
l=len(s)
i=0
while i<start and i<l:
if is_surrogate(s,i):
start+=1
end+=1
i+=1
i+=1
while i<end and i<l:
if is_surrogate(s,i):
end+=1
i+=1
i+=1
return s[start:end]
Tutaj cena płacisz jest wydajność , ponieważ funkcje te są znacznie wolniejsze niż wbudowane:
>>> ux=u"a"*5000+u"\U00100000"*30000+u"b"*50000
>>> timeit.timeit('slice(ux,10000,100000)','from __main__ import slice,ux',number=1000)
46.44128203392029 #msec
>>> timeit.timeit('ux[10000:100000]','from __main__ import slice,ux',number=1000000)
8.814016103744507 #usec
Zamknąłem to jako duplikat pytania typu "nadzorca". Przejrzyj odpowiedź wyraźnie. Jeśli to nadal nie rozwiązuje problemu, proszę [edytuj] wpis, aby uwzględnić dodatkowe próby. –
Moje pytanie różni się od drugiego tym, że mam do czynienia z ciągami zawierającymi kombinację znaków emoji i znaków innych niż emoji. Poza tym nie jestem zainteresowany liczeniem emotikonów, ale otrzymuję listę wszystkich postaci. – Aaron
Aby było jasne, lista, którą otrzymałeś, jest poprawna. Chodzi o to, że jeśli drukujesz 'list', to pokazuje' repr' zawartości, a nie 'str' postaci; musisz wydrukować poszczególne wpisy ręcznie, aby zobaczyć formularz 'str' (który wyglądałby jak emoji). Na przykład, jeśli zrobisz "print (u", ".join (char_list))" zobaczysz, czego oczekujesz bez nawiasów prowadzących lub końcowych. – ShadowRanger