Unicode (UTF-8) odczyt i zapis do plików w Pythonie

Mam pewną awarię mózgu w zrozumieniu czytania i pisania tekstu do pliku (Python 2.4).

# The string, which has an a-acute in it.
ss = u'Capit\xe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)

("U' Capit\xe1n'", "'Capit\xc3\xa1n'")

print ss, ss8
print >> open('f1','w'), ss8

>>> file('f1').read()
'Capit\xc3\xa1n\n'

Więc wpisuję Capit\xc3\xa1n do mojego ulubionego edytora, w pliku f2.

Wtedy:

>>> open('f1').read()
'Capit\xc3\xa1n\n'
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'
>>> open('f1').read().decode('utf8')
u'Capit\xe1n\n'
>>> open('f2').read().decode('utf8')
u'Capit\\xc3\\xa1n\n'
Czego tu nie rozumiem? Najwyraźniej jest jakiś istotny kawałek magii (lub dobrego zmysłu), którego mi brakuje. Co należy wpisać do plików tekstowych, aby uzyskać odpowiednie konwersje?

Czego naprawdę nie potrafię zgrzytać oto, jaki jest sens reprezentacji UTF-8, jeśli nie możesz zmusić Pythona do jej rozpoznania, jeśli pochodzi ona z zewnątrz. Może powinienem po prostu zrzucić łańcuch JSON i użyć go zamiast tego, ponieważ ma on reprezentację asciable! Co więcej, Czy istnieje reprezentacja ASCII tego obiektu Unicode, którą Python rozpoznaje i dekoduje, gdy wchodzi z pliku? Jeśli tak, to jak go zdobyć?

>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'
Author: Peter Mortensen, 2009-01-29

13 answers

W notacji

u'Capit\xe1n\n'

"\xe1 " reprezentuje tylko jeden bajt. "\x "mówi, że" e1 " jest w systemie szesnastkowym. Kiedy piszesz

Capit\xc3\xa1n

W Twoim pliku masz "\xc3". Są to 4 bajty, a w kodzie czytasz je wszystkie. Możesz to zobaczyć, gdy je wyświetlasz:

>>> open('f2').read()
'Capit\\xc3\\xa1n\n'

Widać, że ukośnik odwrotny jest unikany przez ukośnik odwrotny. Więc masz cztery bajty w łańcuchu:"\", "x", " c " i "3".

Edit:

Jak inni wskazywali w swoich odpowiedziach ty należy tylko wpisać znaki w edytorze, a następnie edytor powinien obsłużyć konwersję do UTF-8 i zapisać ją.

Jeśli rzeczywiście masz ciąg znaków w tym formacie, możesz użyć kodeka string_escape, aby zdekodować go do normalnego ciągu znaków:

In [15]: print 'Capit\\xc3\\xa1n\n'.decode('string_escape')
Capitán

Wynikiem jest łańcuch, który jest zakodowany w UTF-8, gdzie znak akcentowany jest reprezentowany przez dwa bajty, które zostały zapisane \\xc3\\xa1 w oryginalnym łańcuchu. Jeśli chcesz mieć ciąg unicode musisz ponownie dekodować za pomocą UTF-8.

Do edycji: nie masz UTF-8 w pliku. Aby faktycznie zobaczyć, jak to będzie wyglądać:

s = u'Capit\xe1n\n'
sutf8 = s.encode('UTF-8')
open('utf-8.out', 'w').write(sutf8)

Porównaj zawartość pliku utf-8.out z zawartością pliku zapisanego w edytorze.

 90
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-29 18:44:41

Zamiast zadzierać z metodami kodowania i dekodowania, łatwiej jest określić kodowanie podczas otwierania pliku. Na io moduł (dodany w Pythonie 2.6) dostarcza funkcję io.open, która ma parametr kodowania.

Użyj metody otwartej z modułu io.

>>>import io
>>>f = io.open("test", mode="r", encoding="utf-8")

Następnie po wywołaniu funkcji read (), zwracany jest zakodowany obiekt Unicode.

>>>f.read()
u'Capit\xe1l\n\n'

Zauważ, że w Pythonie 3 Funkcja io.open jest aliasem dla wbudowanej funkcji open. Wbudowana funkcja open obsługuje tylko argument kodowania w Pythonie 3, a nie Pythonie 2.

Edit: poprzednio ta odpowiedź zalecała moduł kodeki. Moduł kodeków może powodować problemy przy mieszaniu read() i readline(), więc ta odpowiedź zaleca teraz moduł io .

Użyj metody open z modułu kodeków.

>>>import codecs
>>>f = codecs.open("test", "r", "utf-8")

Następnie po wywołaniu funkcji read (), zakodowany obiekt Unicode jest zwrócony.

>>>f.read()
u'Capit\xe1l\n\n'

Jeśli znasz kodowanie pliku, używanie pakietu kodeków będzie znacznie mniej mylące.

Zobacz http://docs.python.org/library/codecs.html#codecs.open

 612
Author: Tim Swast,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-03 22:16:04

Teraz wszystko czego potrzebujesz w Python3 to open(Filename, 'r', encoding='utf-8')

[Edit on 2016-02-10 for requested clarification]

Python3 dodał parametr kodowanie do swojej funkcji otwartej. Poniżej znajdują się informacje o funkcji otwartej: https://docs.python.org/3/library/functions.html#open

open(file, mode='r', buffering=-1, 
      encoding=None, errors=None, newline=None, 
      closefd=True, opener=None)

Encoding to nazwa kodowania używanego do dekodowania lub kodowania plik. To powinno być używane tylko w trybie tekstowym. Domyślne kodowanie na Platforma zależna (niezależnie locale.getpreferredencoding () zwraca), ale można użyć dowolnego kodowania tekstu wspieranego przez Pythona. Lista obsługiwanych kodowań znajduje się w module kodeki.

Więc dodając encoding='utf-8' jako parametr do funkcji open, odczyt i zapis pliku odbywa się jako utf8 (który jest teraz również domyślnym kodowaniem wszystkiego, co robi się w Pythonie.)

 20
Author: Dakusan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 08:20:30

Więc znalazłem rozwiązanie tego, czego szukam, czyli:

print open('f2').read().decode('string-escape').decode("utf-8")

Jest kilka niezwykłych kodeków, które są tutaj przydatne. Ten konkretny odczyt pozwala na pobranie reprezentacji UTF-8 z Pythona, skopiowanie ich do pliku ASCII i wczytanie ich do Unicode. Pod dekoderem" string-escape " ukośniki nie zostaną podwojone.

To pozwala na taką podróż w obie strony, jaką sobie wyobrażałem.
 16
Author: Gregg Lind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 18:37:29

Właściwie, to zadziałało dla mnie do czytania pliku z kodowaniem UTF-8 w Pythonie 3.2:

import codecs
f = codecs.open('file_name.txt', 'r', 'UTF-8')
for line in f:
    print(line)
 13
Author: Sina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 18:43:01
# -*- encoding: utf-8 -*-

# converting a unknown formatting file in utf-8

import codecs
import commands

file_location = "jumper.sub"
file_encoding = commands.getoutput('file -b --mime-encoding %s' % file_location)

file_stream = codecs.open(file_location, 'r', file_encoding)
file_output = codecs.open(file_location+"b", 'w', 'utf-8')

for l in file_stream:
    file_output.write(l)

file_stream.close()
file_output.close()
 12
Author: Ricardo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-08 20:24:46

Aby przeczytać w ciągu Unicode, a następnie wysłać do HTML, zrobiłem to:

fileline.decode("utf-8").encode('ascii', 'xmlcharrefreplace')

Przydatne dla serwerów http opartych na Pythonie.

 5
Author: praj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-18 14:38:14

Z wyjątkiem codecs.open(), można użyć io.open() do pracy z Python2 lub Python3 do odczytu / zapisu pliku unicode

Przykład

import io

text = u'á'
encoding = 'utf8'

with io.open('data.txt', 'w', encoding=encoding, newline='\n') as fout:
    fout.write(text)

with io.open('data.txt', 'r', encoding=encoding, newline='\n') as fin:
    text2 = fin.read()

assert text == text2
 5
Author: Ryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-18 01:32:24

Cóż, Twój ulubiony edytor tekstu nie zdaje sobie sprawy, że \xc3\xa1 powinny być literałami znaków, ale interpretuje je jako tekst. Dlatego dostajesz podwójne ukośniki w ostatniej linii - teraz jest to prawdziwy ukośnik + xc3, itp. w Twoich aktach.

Jeśli chcesz czytać i pisać zakodowane pliki w Pythonie, najlepiej użyj modułu kodeki.

Wklejanie tekstu między terminalem a aplikacjami jest trudne, ponieważ nie wiesz, który program zinterpretuje Twój tekst za pomocą jakiego kodowania. Możesz spróbować:

>>> s = file("f1").read()
>>> print unicode(s, "Latin-1")
Capitán

Następnie wklej ten ciąg do edytora i upewnij się, że przechowuje go za pomocą Latin-1. Przy założeniu, że Schowek nie przepełnia Sznurka, podróż w obie strony powinna działać.

 4
Author: Torsten Marek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 18:11:07

Natknąłeś się na ogólny problem z kodowaniem: Skąd mogę wiedzieć, w którym kodowaniu jest plik?

Odpowiedź: nie możesz chyba że przewiduje to format pliku. XML, na przykład, zaczyna się od:

<?xml encoding="utf-8"?>

Ten nagłówek został starannie dobrany, aby można go było odczytać bez względu na kodowanie. W Twoim przypadku nie ma takiej podpowiedzi, stąd ani twój edytor, ani Python nie mają pojęcia, co się dzieje. Dlatego należy użyć modułu codecs i użyć codecs.open(path,mode,encoding), który dostarcza brakujący bit w Pythonie.

Jeśli chodzi o edytor, musisz sprawdzić, czy Oferuje on jakiś sposób na Ustawienie kodowania pliku.

Celem UTF-8 jest możliwość kodowania 21-bitowych znaków (Unicode) jako 8-bitowego strumienia danych (ponieważ jest to jedyna rzecz, z którą mogą sobie poradzić wszystkie komputery na świecie). Ale ponieważ większość OSs sprzed ery Unicode, nie mają odpowiednich narzędzi do dołączania informacji o kodowaniu do plików na dysku twardym.

Następny numer to reprezentacja w Pythonie. Jest to doskonale wyjaśnione w komentarz heikogerlach. Musisz zrozumieć, że Twoja konsola może wyświetlać tylko ASCII. Aby wyświetlić Unicode lub cokolwiek > = charcode 128, musi użyć pewnych sposobów ucieczki. W edytorze nie wolno wpisywać napisu wyświetlającego, ale co on oznacza (w tym przypadku należy wpisać umlaut i zapisać plik).

To powiedziawszy, możesz użyć funkcji eval () w Pythonie, aby przekształcić znak ucieczki w string:

>>> x = eval("'Capit\\xc3\\xa1n\\n'")
>>> x
'Capit\xc3\xa1n\n'
>>> x[5]
'\xc3'
>>> len(x[5])
1

Jak widać, łańcuch "\xc3 " został przekształcony w pojedynczy znak. Jest to obecnie 8-bitowy ciąg znaków, kodowany UTF-8. Aby uzyskać Unicode:

>>> x.decode('utf-8')
u'Capit\xe1n\n'

Gregg Lind zapytał: myślę, że brakuje tu kilku kawałków: plik f2 zawiera: hex:

0000000: 4361 7069 745c 7863 335c 7861 316e  Capit\xc3\xa1n

codecs.open('f2','rb', 'utf-8'), na przykład, odczytuje je wszystkie w osobnych znakach (oczekiwane) czy jest jakiś sposób, aby zapisać do pliku w ASCII, który by działał?

Odpowiedź: to zależy co masz na myśli. ASCII nie może reprezentują znaki > 127. Więc musisz w jakiś sposób powiedzieć "kilka następnych znaków oznacza coś specjalnego", co robi Sekwencja "\x". Jest napisane: następne dwa znaki są kodem pojedynczego znaku. "\u " robi to samo używając czterech znaków do kodowania Unicode do 0xFFFF (65535).

Więc nie można bezpośrednio zapisać Unicode do ASCII (ponieważ ASCII po prostu nie zawiera tych samych znaków). Można go zapisać jako string escapes (jak w f2); w tym przypadku plik może być reprezentowana jako ASCII. Możesz też zapisać go jako UTF-8, w którym to przypadku potrzebujesz 8-bitowego bezpiecznego strumienia.

Twoje rozwiązanie przy użyciu decode('string-escape') działa, ale musisz być świadomy, ile pamięci zużywasz: trzy razy więcej niż używasz codecs.open().

Pamiętaj, że plik jest tylko sekwencją bajtów z 8 bitami. Ani bity, ani bajty nie mają znaczenia. To ty mówisz "65 znaczy "A". Ponieważ \xc3\xa1 powinno stać się "à", ale komputer nie ma możliwości poznania, musisz to powiedzieć, podając kodowanie, które zostało użyte podczas zapisu pliku.

 4
Author: Aaron Digulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:25

\x.. sekwencja jest czymś specyficznym dla Pythona. To nie jest uniwersalna sekwencja ucieczki bajtów.

To, w jaki sposób wpisujesz kodowane w UTF-8 nie-ASCII, zależy od Twojego systemu operacyjnego i / lub edytora. Oto Jak to zrobić w Windows . Aby OS X mógł wprowadzić a z akcentem ostrym, wystarczy nacisnąć opcja + E , Następnie A , a prawie wszystkie edytory tekstu w OS X obsługują UTF-8.

 3
Author: ʞɔıu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 18:09:13

Możesz również poprawić oryginalną funkcję open() do pracy z plikami Unicode, zastępując ją na swoim miejscu za pomocą funkcji partial. Piękno tego rozwiązania polega na tym, że nie trzeba zmieniać żadnego starego kodu. Jest przezroczysty.

import codecs
import functools
open = functools.partial(codecs.open, encoding='utf-8')
 2
Author: hipertracker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 18:47:44

Próbowałem parsować iCal używając Pythona 2.7.9:

Z kalendarza importu icalendar

Ale ja dostawałem:

 Traceback (most recent call last):
 File "ical.py", line 92, in parse
    print "{}".format(e[attr])
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 7: ordinal not in range(128)

I został naprawiony za pomocą just:

print "{}".format(e[attr].encode("utf-8"))

(Teraz może drukować jak á böss.)

 0
Author: Alexx Roche,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 18:45:31