Różnica między open i codecs.open w Pythonie

97

Istnieją dwa sposoby otwarcia pliku tekstowego w Pythonie:

f = open(filename)

I

import codecs
f = codecs.open(filename, encoding="utf-8")

Kiedy codecs.openlepiej open?

BlogueroConnor
źródło
46
Zauważ, że codecs.open()jest to przestarzałe w 3.x, ponieważ open()zyskuje encodingargument.
Ignacio Vazquez-Abrams
Jest też trzeci sposób (przynajmniej w Pythonie 2.x): `f = plik (nazwa pliku) '
Adam Parkin
1
@ IgnacioVazquez-Abrams Czy istnieje link, który codecs.open()jest przestarzały? Nie sądzę, żeby to było w dokumentach Python3
varela
1
@varela: wspomniana strona dokumentacji Pythona mówi: „wbudowany open () i powiązany moduł io to zalecane podejście do pracy z zakodowanymi plikami tekstowymi”
Luciano Ramalho

Odpowiedzi:

83

Od Pythona 2.6, dobrą praktyką jest używanie io.open(), które również przyjmuje encodingargument, tak jak teraz przestarzały codecs.open(). W Pythonie 3 io.openjest aliasem dla open()wbudowanego. io.open()Działa więc w Pythonie 2.6 i wszystkich późniejszych wersjach, w tym w Pythonie 3.4. Zobacz dokumentację: http://docs.python.org/3.4/library/io.html

A teraz, jeśli chodzi o pierwotne pytanie: podczas czytania tekstu (w tym „zwykłego tekstu”, HTML, XML i JSON) w Pythonie 2 należy zawsze używać io.open()z jawnym kodowaniem lub open()z jawnym kodowaniem w Pythonie 3. Czyniąc to, uzyskasz poprawny wynik zdekodować Unicode lub otrzymać błąd od razu, co znacznie ułatwia debugowanie.

Czysty ASCII „zwykły tekst” to mit z odległej przeszłości. Prawidłowy tekst w języku angielskim używa cudzysłowów, myślników, punktorów, € (znaki euro), a nawet dierezy (¨). Nie bądź naiwny! (I nie zapominajmy o wzorcu projektowym elewacji!)

Ponieważ czysty ASCII nie jest prawdziwą opcją, open()bez jawnego kodowania jest przydatny tylko do odczytu plików binarnych .

Luciano Ramalho
źródło
5
@ForeverWintr Odpowiedź jest tam dość jasna: użyj io.open()dla tekstu i open()tylko dla plików binarnych. Wniosek jest taki, że codecs.open()wcale nie jest to preferowane.
Bdoserror
2
@Bdoserror, Jest odpowiedź tam wyraźnie, ale to nie jest odpowiedź na pytanie, które zostało zadane. Pytanie dotyczyło różnicy między i , a konkretnie, kiedy to drugie jest lepsze od pierwszego. Odpowiedź, która nawet nie wspomina, nie może odpowiedzieć na to pytanie. opencodecs.opencodecs.open
ForeverWintr
3
@ForeverWintr Jeśli OP zapytał źle pytanie (tj. Z założeniem, że codecs.open()było poprawne w użyciu), nie ma „poprawnej” odpowiedzi na temat tego, kiedy go użyć. Odpowiedź brzmi: użyj io.open()zamiast tego. To tak, jakby zapytać „kiedy powinienem wbić gwóźdź w ścianę kluczem?”. Prawidłowa odpowiedź to „użyj młotka”.
Bdoserror
20

Osobiście zawsze używam, codecs.openchyba że istnieje jasno określona potrzeba użycia open**. Powodem jest to, że tyle razy zostałem ugryziony przez wejście utf-8, które wkradło się do moich programów. „Och, po prostu wiem, że to zawsze będzie ascii” to założenie, które często się łamie.

Zakładanie „utf-8” jako domyślnego kodowania wydaje się być bezpieczniejszym domyślnym wyborem z mojego doświadczenia, ponieważ ASCII można traktować jako UTF-8, ale odwrotność nie jest prawdą. A w tych przypadkach, kiedy naprawdę wiem, że dane wejściowe to ASCII, nadal robię to, codecs.openponieważ mocno wierzę w „wyraźne jest lepsze niż ukryte” .

** - w Pythonie 2.x, ponieważ openzastępuje komentarz do stanów pytania w Pythonie 3codecs.open

Adam Parkin
źródło
to, czego tak naprawdę nie rozumiem, to dlaczego openczasami radzę sobie bardzo dobrze ze
znakami niełacińskimi
To ma dla mnie sens. io.opennie pobiera parametru kodowania z tego, co widzę w Pythonie 2.7.5
radtek
1
@radtek, masz rację, że jest to nieudokumentowane; jednak (przynajmniej w 2.7.12) io.openakceptuje encodingi newlineparametry i interpretuje je tak, jak robi to Python 3. W przeciwieństwie do tego codecs.open, plik otwarty za pomocą io.openpodniesie się TypeError: write() argument 1 must be unicode, not strnawet w Pythonie 2.7, jeśli spróbujesz do niego napisać str( bytes). Plik otwarty za pomocą codecs.openzamiast tego będzie próbował niejawnej konwersji do unicode, co często prowadzi do mylących plików UnicodeDecodeErrors.
jochietoch
9

W Pythonie 2 istnieją ciągi znaków Unicode i bajty. Jeśli używasz tylko bytestringów, możesz czytać / zapisywać do pliku otwartego w open()porządku. W końcu łańcuchy to tylko bajty.

Problem pojawia się, gdy, powiedzmy, masz ciąg znaków Unicode i wykonujesz następujące czynności:

>>> example = u'Μου αρέσει Ελληνικά'
>>> open('sample.txt', 'w').write(example)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

Więc tutaj oczywiście albo jawnie kodujesz swój ciąg znaków Unicode w utf-8, albo codecs.openrobisz to za siebie w sposób przejrzysty.

Jeśli używasz tylko bajtestów, nie ma problemów:

>>> example = 'Μου αρέσει Ελληνικά'
>>> open('sample.txt', 'w').write(example)
>>>

Wiąże się to bardziej niż to, ponieważ kiedy łączysz Unicode i testujesz łańcuch z +operatorem, otrzymujesz ciąg znaków Unicode. Łatwo się przez to ugryźć.

Również codecs.opennie lubi bytestrings ze znakami spoza ASCII są przekazywane w:

codecs.open('test', 'w', encoding='utf-8').write('Μου αρέσει')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/codecs.py", line 691, in write
    return self.writer.write(data)
  File "/usr/lib/python2.7/codecs.py", line 351, in write
    data, consumed = self.encode(object, self.errors)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xce in position 0: ordinal not in range(128)

Porada dotycząca ciągów znaków wejściowych / wyjściowych jest zwykle „konwertowana do formatu Unicode tak wcześnie, jak to możliwe i z powrotem do bajtestów tak późno, jak to możliwe”. Użycie codecs.openpozwala bardzo łatwo zrobić to drugie.

Po prostu uważaj, aby nadawać mu ciągi znaków Unicode, a nie bajty, które mogą zawierać znaki spoza ASCII.

Żuchwa79
źródło
Czy możesz wyjaśnić swój drugi przykład? Wygląda na to, że jest identyczny z pierwszym przykładem, więc dlaczego wynik miałby być inny?
Chris Johnson,
Zwróć uwagę na użycie u''w pierwszym przykładzie. Oznacza to, że utworzyłem ciąg znaków Unicode, a nie bajt. Na tym polega różnica między tymi dwoma przykładami. W drugim przykładzie tworzę bajtestowanie i zapisanie jednego z nich do pliku jest w porządku. Ciąg znaków Unicode nie jest w porządku, jeśli używasz znaków spoza ASCII.
Żuchwa79
7

Gdy potrzebujesz otworzyć plik, który ma określone kodowanie, użyjesz codecsmodułu.

Geo
źródło
15
Wydaje
5

codecs.openPrzypuszczam, że to pozostałość po Python 2czasach, kiedy wbudowany otwarty miał znacznie prostszy interfejs i mniej możliwości. W Pythonie 2 openfunkcja wbudowana nie pobiera argumentu kodowania, więc jeśli chcesz użyć czegoś innego niż tryb binarny lub domyślne kodowanie, powinno zostać użyte codecs.open.

W Python 2.6roku moduł io przyszedł z pomocą, aby nieco uprościć sprawę. Według oficjalnej dokumentacji

New in version 2.6.

The io module provides the Python interfaces to stream handling.
Under Python 2.x, this is proposed as an alternative to the
built-in file object, but in Python 3.x it is the default
interface to access files and streams.

Powiedziawszy to, jedyne zastosowanie, jakie przychodzi mi do głowy codecs.openw obecnym scenariuszu, to kompatybilność wsteczna. We wszystkich innych scenariuszach (chyba że używasz Pythona <2.6) lepiej jest używać io.open. Również w Python 3.x io.openjest to samo cobuilt-in open

Uwaga:

Istnieje również różnica składniowa między codecs.openi io.open.

codecs.open:

open(filename, mode='rb', encoding=None, errors='strict', buffering=1)

io.open:

open(file, mode='r', buffering=-1, encoding=None,
     errors=None, newline=None, closefd=True, opener=None)
heretolearn
źródło
Nie tylko codecs.openi io.openróżnią się składnią, ale zwracają obiekty różnego typu. codecs.openDziała również zawsze z plikami w trybie binarnym.
wombatonfire
4
  • Jeśli chcesz załadować plik binarny, użyj f = io.open(filename, 'b').

  • Aby otworzyć plik tekstowy, zawsze używaj f = io.open(filename, encoding='utf-8')z jawnym kodowaniem.

W Pythonie 3 jednak opennie to samo, co io.openi może być stosowany zamiast.

Uwaga: codecs.open planuje się stać przestarzałe i zastąpione przez io.openpo wprowadzeniu w Pythonie 2.6 . Użyłbym go tylko wtedy, gdy kod musi być zgodny z wcześniejszymi wersjami Pythona. Aby uzyskać więcej informacji na temat kodeków i Unicode w Pythonie, zobacz Unicode HOWTO .

wihlke
źródło
1. Dlaczego nie mogę otworzyć pliku w trybie binarnym za pomocą io.openlub codecs.open? 2. codecs.opennie jest jeszcze przestarzała, przeczytaj dyskusję na stronie, do której masz łącze.
wombatonfire
Słuszne uwagi! 1. Możesz użyć jednego z nich, ale ponownie odradzałbym codecs.open, chyba że używasz Pythona 2.5 lub starszego. 2. Zaktualizowałem swoją odpowiedź, aby odzwierciedlić, że wycofanie nie nastąpiło natychmiast, ale raczej w przyszłości.
wihlke
3

Kiedy pracujesz z plikami tekstowymi i potrzebujesz przezroczystego kodowania i dekodowania do obiektów Unicode.

Cat Plus Plus
źródło
0

Miałem okazję otworzyć plik .asm i go przetworzyć.

#https://docs.python.org/3/library/codecs.html#codecs.ignore_errors
#https://docs.python.org/3/library/codecs.html#codecs.Codec.encode
with codecs.open(file, encoding='cp1252', errors ='replace') as file:

Bez większego problemu jestem w stanie odczytać cały plik, jakieś sugestie?

Gowtham Ch
źródło