2016-02-15 21 views
18

Pracuję w Pythonie 2 i mam ciąg znaków zawierający emotikony oraz inne znaki Unicode. Muszę przekonwertować go na listę, gdzie każdy wpis na liście jest pojedynczym znakiem/emoji.Poprawnie wyodrębnij emotikony z ciągu Unicode

x = u'xyz' 
char_list = [c for c in x] 

Pożądana jest wyjście:

['', '', 'x', 'y', 'z', '', ''] 

Rzeczywista moc wynosi:

[u'\ud83d', u'\ude18', u'\ud83d', u'\ude18', u'x', u'y', u'z', u'\ud83d', u'\ude0a', u'\ud83d', u'\ude0a'] 

jaki sposób można osiągnąć pożądany wynik?

+0

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. –

+0

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

+0

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

Odpowiedz

15

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 
+2

Należy pamiętać, że ze wszystkimi nowymi dodatkami do emoji jest to nieco zepsute, ponieważ niektóre emoji składają się z wielu punktów kodowych. Przykłady obejmują flagi ('' "') i warianty etnyczne ('" "' '' '' ') oraz kilka innych rzeczy, takich jak łączenie znaków diakrytycznych' "à" '. – roeland

+0

@roeland to 'is_surrogate' musi zostać uaktualniony, aby wykryć je i zwrócić liczbę dodatkowych słów (= 2-bajtowe znaki) zamiast True/False. To jest pod warunkiem, że jesteśmy zainteresowani takimi przypadkami (znaki kontrolne i znaki diakrytyczne są zupełnie inną sprawą, jeśli pytasz mnie), a inne udogodnienia, takie jak normalizacja, nie mogą wykonać tego zadania. –

+2

Nie sądzę, że normalizacja zajmie się tymi emotikonami. Dokładnie poprawna odpowiedź będzie powtarzać się nad klastrami grafem, długie i tajemnicze wyjaśnienie w [Standardowy Załącznik nr 29 Unicode®] (http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules). Ale bez biblioteki, która poradzi sobie z tym, prawdopodobnie trzymałbym się iteracji punktów kodu. – roeland

8

chciałbym użyć uniseg biblioteki (pip install uniseg):

# -*- coding: utf-8 -*- 
from uniseg import graphemecluster as gc 

print list(gc.grapheme_clusters(u'xyz')) 

wyjść [u'\U0001f618', u'\U0001f618', u'x', u'y', u'z', u'\U0001f60a', u'\U0001f60a'] i

[x.encode('utf-8') for x in gc.grapheme_clusters(u'xyz'))] 

dostarczy listę znaków jako UTF-8 strun.

+1

Twoja odpowiedź nie drukuje żądanego wyjścia – otorrillas

+1

Ok, dodam konwersję, by dokładnie określić, o co pytam. –

+0

@James Hopkin możesz podać sposób, w jaki możemy konwertować te emisje na Unicode, np. Na u '\ U0001f618' w python 3 –

Powiązane problemy