Prima di tutto, in Python2, è necessario utilizzare le stringhe Unicode (u'<...>'
) per i caratteri Unicode da visualizzare come caratteri Unicode. E correct source encoding se si desidera utilizzare gli stessi caratteri anziché la rappresentazione \UXXXXXXXX
nel codice sorgente.
Ora, secondo Python: getting correct string length when it contains surrogate pairs e Python returns length of 2 for single Unicode character string, in python2 "stretta" build (con sys.maxunicode==65535
), caratteri Unicode a 32 bit sono rappresentati come surrogate pairs, e questo non è trasparente alle funzioni di stringa. Questo è stato corretto solo in 3.3 (PEP0393).
La risoluzione più semplice (salvare per migrare a 3.3+) è compilare una compilazione "wide" di Python dal sorgente come indicato nel terzo link. In esso, i caratteri Unicode sono tutti a 4 byte (quindi sono un potenziale maiale della memoria) ma se è necessario gestire in modo sistematico caratteri larghi Unicode, questo è probabilmente un prezzo accettabile.
La soluzione per una "stretta" costruire è per fare un set personalizzato di funzioni di stringa (len
, slice
, forse come una sottoclasse di unicode
), che rileva le coppie di surrogati e gestirli come un singolo carattere.Non ho potuto facilmente trovare uno esistente (che è strano), ma non è troppo difficile da scrivere:
- secondo UTF-16#U+10000 to U+10FFFF - Wikipedia,
- il 1 ° carattere (surrogato alto) è nella gamma
0xD800..0xDBFF
- il 2 ° carattere (surrogato basso) - nella gamma
0xDC00..0xDFFF
- questi intervalli sono riservati e quindi non può verificarsi come personaggi regolari
Quindi, ecco il codice per rilevare una coppia di surrogati:
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
e una funzione che restituisce una semplice fetta:
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]
Qui, il prezzo da pagare è la prestazione , poiché queste funzioni sono molto più lente dei built-in:
>>> 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
L'ho chiuso come duplicato di una domanda sul superset. Passa attraverso la risposta loro chiaramente. Se ancora non risolve il tuo problema, per favore [modifica] il post per includere i tuoi ulteriori tentativi. –
La mia domanda differisce dall'altra in quanto ho a che fare con stringhe che contengono un mix di emoji e personaggi non emoji. Inoltre, non sono interessato a contare gli emoji ma a ottenere un elenco di tutti i personaggi. – Aaron
Per essere chiari, la lista che hai è corretta. È solo che se si stampa un 'elenco' mostra il' repr' del contenuto, non la forma 'str'; è necessario stampare manualmente le singole voci per vedere la forma 'str' (che sembrerebbe un'emoji). Ad esempio, se si esegue 'print (u ',' .join (char_list))' vedrete cosa vi aspettate senza parentesi iniziali o finali. – ShadowRanger