Znalazłem rozwiązanie na podobne pytanie na inny temat, ale niestety u mnie nie działa. Oto mój problem:

Tworzę ramkę danych z unicodes par zastępczych, które chciałbym wyszukać w innym pliku (na przykład: „\ uD83C \ uDFF3”, „\ u26F9”, „\ uD83C \ uDDE6 \ uD83C \ uDDE8”):

    with open("unicodes.csv", "rt") as csvfile:                                        
        emoticons = pd.read_csv(csvfile, names=["xy"])
        emoticons = pd.DataFrame(emoticons)
        emoticons = emoticons.astype(str)

Następnie czytam mój plik z tekstem, w którym niektóre wiersze zawierają zastępcze pary unicodes:

    for chunk in pd.read_csv(path, names=["xy"], encoding="utf-8", chunksize=chunksize):            
        spam = pd.DataFrame(chunk)
        spam = spam.astype(str)

W tej pętli for sprawdzam, czy wiersz zawiera surogatepairs unicode, a jeśli to prawda, to chciałbym wydrukować tę surogatepair unicode jako emoji - dlatego koduję i dekoduję tę wartość "i", która jest str: (rozwiązanie z: Jak pracować z parami zastępczymi w Pythonie?)

        for i in emoticons.xy:
            if spam["xy"].str.contains(i, regex=False).any():                                 
                print(i.encode('utf-16', 'surrogatepass').decode('utf-16'))

               #printing:
               #\uD83C\uDFF3 
               #\u26F9
               #\uD83C\uDDE6\uD83C\uDDE8

Tak więc, kiedy uruchamiam program, nadal drukuje on pary zastępcze unicode jako str, a nie jako emoji, ale kiedy samodzielnie wprowadzam unicode pary zastępczej do funkcji drukowania, działa:

    print("\uD83C\uDFF3".encode("utf-16", "surrogatepass").decode("utf-16", "surrogatepass"))

    #printing:
    #🏳 

Co ja robię źle? Próbowałem utworzyć ciąg z tego i innych rozwiązań, ale nadal nie działa.

EDYTOWAĆ:

hexdump -C file.csv
00004b70  5c 75 44 38 33 44 5c 75  44 45 45 39 0a 5c 75 44  |\uD83D\uDEE9.\uD|
00004b80  38 33 44 5c 75 44 45 45  42 0a 5c 75 44 38 33 44  |83D\uDEEB.\uD83D|
00004b90  5c 75 44 45 45 43 0a 5c  75 44 38 33 44 5c 75 44  |\uDEEC.\uD83D\uD|
00004ba0  43 42 41 0a 5c 75 44 38  33 44 5c 75 44 45 38 31  |CBA.\uD83D\uDE81|

EDYCJA2: Więc znalazłem coś, co działa, ale nadal potrzebuję poprawy: https://stackoverflow.com/a/54918256/4789281

Tekst z mojego innego pliku, który chcę przekonwertować plik wygląda:

"O żółtku zapomniałaś \uD83D\uDE02"
"Piękny outfit \uD83D\uDE0D"

Kiedy robię to, co było zalecane w innym temacie:

print(codecs.decode(i,encoding='unicode_escape',errors='surrogateescape').encode('utf-16', 'surrogatepass').decode('utf-16'))

Mam coś takiego:

O żóÅtku zapomniaÅaÅ 😂
PiÄkny outfit 😍

Więc moje zastępcze pary są zastępowane, ale moje polskie znaki są zastępowane czymś dziwnym.

0
maliniaki 20 listopad 2019, 10:42
Surogaty nie są dozwolone w UTF-8 — mimo że niektóre implementacje obsługują to, ale nie ta w Pythonie, która trzyma się standardu. Ponadto zwykle nie jest przydatne umieszczanie surragtes w ciągach, więc "\uD83C\uDFF3" jest poprawnie reprezentowane jako '\U0001f3f3'. Jeśli jednak masz dane zserializowane jako (nieprawidłowe) UTF-8 z surogatami i nie możesz naprawić źródła, to podaj to wyraźnie (np. pokaż fragment zrzutu szesnastkowego takiego regionu w dokumencie wejściowym).
 – 
lenz
20 listopad 2019, 12:48
@lenz, więc zrobiłem coś takiego podczas drukowania: print(hexdump.hexdump(i.encode())) #printing: 00000000: 5C 75 44 38 33 43 5C 75 44 44 45 41 5C 75 44 38 \uD83C \uDDEA\uD8 00000010: 33 43 5C 75 44 44 46 41 3C\uDDFA Brak
 – 
maliniaki
20 listopad 2019, 15:08
Czy możesz to zadać w pytaniu? Trudno to tak czytać. Ponadto bardziej pouczające może być posiadanie zrzutu heksowego nieprzetworzonego pliku (ten sam fragment) niż wstępnie przetworzonego przez pandy.
 – 
lenz
20 listopad 2019, 17:59
@lenz, zrobiłem hexdump w pliku. Niektóre dane wyjściowe są w edycji pytania
 – 
maliniaki
21 listopad 2019, 11:24
Twój plik „file.csv” nie zawiera par zastępczych. Jest to zwykły ASCII: zawiera sekwencje specjalne, tj. dosłownie znaki ` (backslash), u, D, 8, 3, D` itd. Czy są przypadkiem osadzone fragmenty JSON w pliku CSV ? Ponieważ JSON używa tej notacji do ucieczki znaków spoza zestawu ASCII i koduje znaki powyżej U + FFFF z zastępczymi kodami ucieczki.
 – 
lenz
21 listopad 2019, 14:07

1 odpowiedź

Jesteś na dobrej drodze. To, co próbujesz zrobić, jest przerywane, ponieważ to, co masz w swoim "str" ​​po przeczytaniu pliku, nie jest "parami zastępczymi" - zamiast tego są zakodowanymi odwrotnymi ukośnikami punktami kodowymi dla twoich par zastępczych, zakodowanymi jako tekst.

To znaczy: sekwencja „5c 75 44 38 33 44” w twoim pliku to RZECZYWISTE znaki ascii „\uD83D” (w sumie 6 znaków), a nie zastępczy punkt kodowy 0xD83D (który po prawidłowym zdekodowaniu wraz z następnym zastępczym kodem „ \uDE0D” będzie pojedynczym znakiem w ciągu).

Część, o której powiedziałem, że jesteś na dobrej drodze, to: naprawdę musisz zakodować to w sekwencję bajtów, a następnie odkodować ją z powrotem. Błędem jest to, że musisz zakodować go za pomocą "latin1" (tylko po to, aby spróbować zachować dowolny inny znak spoza ascii, który masz w ciągu - może się on zepsuć, jeśli masz punkty kodowe, których nie można przedstawić w języku łacińskim1), i zdekoduj go z powrotem za pomocą specjalny kodek „unicode escape”. lub kodowanie charmap, które zachowuje twoje inne znaki w łańcuchu, a następnie dekoduje je z powrotem, używając tego samego kodeka. W tym momencie oba znaki zastępcze będą tekstem jako dwa znaki w łańcuchu Pythona:

In [16]: "\\uD83D\\uDE0D".encode("latin1").decode("unicode escape", "surrogatepass")                              
Out[16]: '\ud83d\ude0d'

Zła wiadomość jest taka - to nie jest bardzo poprawna STR - znaki zastępcze nie powinny istnieć same w wewnętrznej reprezentacji - zamiast tego powinny być połączone w pożądany końcowy znak. Tak więc próba wydrukowania tego zepsuje:

In [19]: a  = "\\uD83D\\uDE0D".encode("utf-8").decode("unicode escape")                                          

In [20]: print(a)                                                                                                       
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
<ipython-input-20-bca0e2660b9f> in <module>
----> 1 print(a)

UnicodeEncodeError: 'utf-8' codec can't encode characters in position 0-1: surrogates not allowed

Użycie tutaj polityki błędów "surrogatepass" nie pomoże - otrzymasz niedrukowalną sekwencję bajtów.

Dlatego po raz drugi musi to zostać "zakodowane" i "odszyfrowane" - tym razem znaki, które masz w tekście, są rzeczywistymi "zastępczymi" punktami kodowymi, które byłyby prawidłowym kodem UTF-16 do zdekodowania. Tak więc ścieżką jest teraz zakodowanie tej sekwencji, brutalne wymuszenie tych znaków za pomocą „surogatepass”, a następnie dekodowanie z powrotem z utf-16 – co w końcu zrozumie parę zastępczą jako pojedynczy znak:

In [30]: a  = "\\uD83D\\uDE0D".encode("unicode escape").decode("unicode escape")                                          

In [31]: a                                                                                                              
Out[31]: '\ud83d\ude0d'

In [32]: b = a.encode("utf-16", "surrogatepass").decode("utf-16")                                                       

In [33]: print(b)                                                                                                       
😍

Podsumowanie:

Czytasz swój plik jako tekst utf-8, aby odczytać możliwe inne znaki spoza ASCII, zakoduj wynik jako „ucieczkę unicode” i odkoduj go z powrotem - to przekonwertuje rozszerzone sekwencje „\uXXXX” czytelne dla człowieka w twoim pliku jako zastępcze punkty kodowe. Następnie konwertujesz go z powrotem do utf-16, mówiąc Pythonowi, aby zignorował surogaty i skopiował następnie "tak jak jest" i dekodował z powrotem z utf-16:

def decode_surrogate_ascii(txt):
    interm = txt.encode("latin1").decode("unicode escape")
    return interm.encode("utf-16", "surrogatepass").decode("utf-16")

Wszystko, co musisz zrobić, to zastosować powyższą funkcję w interesujących kolumnach w ramce danych:

emoticons = emoticons.apply(pd.Series(lambda row: (decode_surrogate_ascii(item) if isinstance(item,  str) else item for item in row ))
1
jsbueno 22 listopad 2019, 19:00