Différence entre open et codecs.open en Python

95

Il existe deux façons d'ouvrir un fichier texte en Python:

f = open(filename)

Et

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

Quand est-il codecs.openpréférable open?

BlogueroConnor
la source
45
Notez que codecs.open()c'est obsolète dans 3.x, car open()gagne un encodingargument.
Ignacio Vazquez-Abrams
Il y a aussi une troisième méthode (en Python 2.x au moins): `f = file (filename) '
Adam Parkin
1
@ IgnacioVazquez-Abrams Existe-t-il un lien codecs.open()obsolète? Je ne pense pas que cela dans les documents python3: docs.python.org/3.7/library/codecs.html
varela
1
@varela: la page de documentation Python que vous avez mentionnée dit: "l'open () intégré et le module io associé sont l'approche recommandée pour travailler avec des fichiers texte encodés"
Luciano Ramalho

Réponses:

82

Depuis Python 2.6, une bonne pratique consiste à utiliser io.open(), qui prend également un encodingargument, comme le maintenant obsolète codecs.open(). Dans Python 3, io.openest un alias pour le open()fichier intégré. Fonctionne donc io.open()avec Python 2.6 et toutes les versions ultérieures, y compris Python 3.4. Voir la documentation: http://docs.python.org/3.4/library/io.html

Maintenant, pour la question d'origine: lors de la lecture de texte (y compris "texte brut", HTML, XML et JSON) en Python 2, vous devez toujours l' utiliser io.open()avec un encodage explicite, ou open()avec un encodage explicite en Python 3. Cela signifie que vous obtenez correctement décodé Unicode, ou obtenir une erreur dès le départ, ce qui facilite grandement le débogage.

Le «texte brut» ASCII pur est un mythe d'un passé lointain. Un texte anglais correct utilise des guillemets bouclés, des tirets em, des puces, des € (signes de l'euro) et même des tracés (¨). Ne soyez pas naïf! (Et n'oublions pas le modèle de conception Façade!)

Parce que ASCII pur est pas une option réelle, open()sans un codage explicite est seulement utile pour lire binaires fichiers.

Luciano Ramalho
la source
5
@ForeverWintr La réponse est assez claire là-dedans: utiliser io.open()pour le texte, et open()uniquement pour le binaire. L'implication est que ce codecs.open()n'est pas du tout préféré.
Bdoserror
2
@Bdoserror, Il y a une réponse là-dedans, clairement, mais ce n'est pas une réponse à la question qui a été posée. La question portait sur la différence entre openet codecs.open, et plus précisément quand ce dernier est préférable au premier. Une réponse qui ne fait pas autant que mentionner codecs.openne peut pas répondre à cette question.
ForeverWintr
3
@ForeverWintr Si l'OP a posé la mauvaise question (c'est-à-dire avec l'hypothèse qu'il codecs.open()était correct d'utiliser), alors il n'y a pas de réponse "correcte" sur le moment de l'utiliser. La réponse est d'utiliser à la io.open()place. C'est comme si je demandais "quand dois-je utiliser une clé pour enfoncer un clou dans un mur?". La bonne réponse est "utiliser un marteau".
Bdoserror
20

Personnellement, je toujours utiliser codecs.openmoins qu'il y ait un besoin identifié clairement à l' utilisation open**. La raison en est qu'il y a eu tellement de fois où j'ai été mordu en ayant une entrée utf-8 se faufiler dans mes programmes. "Oh, je sais juste que ce sera toujours ascii" a tendance à être une hypothèse qui est souvent rompue.

En supposant 'utf-8' comme encodage par défaut, cela a tendance à être un choix par défaut plus sûr selon mon expérience, car ASCII peut être traité comme UTF-8, mais l'inverse n'est pas vrai. Et dans ces cas où je sais vraiment que l'entrée est ASCII, alors je fais toujours codecs.opencomme je suis un fervent partisan de "l'explicite vaut mieux que l'implicite" .

** - en Python 2.x, car le commentaire sur la question indique dans Python 3 openremplacecodecs.open

Adam Parkin
la source
ce que je ne comprends pas vraiment, c'est pourquoi je openpeux parfois très bien gérer les caractères non latins encodés en UTF-8 de l'ensemble unicode, et parfois cela échoue
misérablement
Cela a du sens pour moi. io.openne prend pas de paramètre d'encodage de ce que je peux voir en python 2.7.5
radtek
1
@radtek, vous avez raison de dire que ce n'est pas documenté; cependant (au moins en 2.7.12) io.openaccepte encodinget newlineparamètres et les interprète comme le fait Python 3. Contrairement à codecs.open, un fichier ouvert avec io.opense lèvera TypeError: write() argument 1 must be unicode, not strmême en Python 2.7 si vous essayez d'écrire str( bytes) dessus. Un fichier ouvert avec codecs.opententera à la place une conversion implicite en unicode, conduisant souvent à la confusion UnicodeDecodeErrors.
jochietoch
9

Dans Python 2, il existe des chaînes et des chaînes d'octets Unicode. Si vous utilisez simplement des chaînes d'octets, vous pouvez lire / écrire dans un fichier ouvert avec open()très bien. Après tout, les chaînes ne sont que des octets.

Le problème survient lorsque, par exemple, vous avez une chaîne Unicode et que vous procédez comme suit:

>>> 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)

Donc ici, évidemment, vous encodez explicitement votre chaîne unicode en utf-8 ou vous l'utilisez codecs.openpour le faire pour vous de manière transparente.

Si vous n'utilisez que des bytestrings, aucun problème:

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

Cela devient plus compliqué que cela car lorsque vous concaténez une chaîne Unicode et une chaîne d'octets avec l' +opérateur, vous obtenez une chaîne Unicode. Facile de se faire mordre par celui-là.

Aussi codecs.openne fonctionne pas comme avec les chaînes d' octets non ASCII caractères sont transmis dans:

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)

Le conseil concernant les chaînes d'entrée / sortie est normalement de "convertir en Unicode le plus tôt possible et de revenir aux chaînes d'octets le plus tard possible". L'utilisation codecs.openvous permet de faire ce dernier très facilement.

Veillez simplement à lui donner des chaînes Unicode et non des chaînes d'octets pouvant contenir des caractères non ASCII.

Mandibule79
la source
Pouvez-vous expliquer votre deuxième exemple? Il semble être identique à votre premier exemple, alors pourquoi le résultat serait-il différent?
Chris Johnson
Notez l'utilisation du u''dans le premier exemple. Cela signifie que j'ai créé une chaîne Unicode, pas une chaîne d'octets. C'est la différence entre les deux exemples. Dans le deuxième exemple, je crée une chaîne d'octets et j'en écris un dans un fichier est très bien. Une chaîne Unicode ne convient pas si vous utilisez des caractères en dehors de ASCII.
Mandible79
7

Lorsque vous avez besoin d'ouvrir un fichier qui a un certain codage, vous utiliserez le codecsmodule.

Géo
la source
15
Je suppose que tous les fichiers texte ont un certain encodage, en quelque sorte (:
cedbeu
5

codecs.open, je suppose, n'est qu'un vestige de l' Python 2époque où l'open intégré avait une interface beaucoup plus simple et moins de capacités. Dans Python 2, intégré openne prend pas d'argument d'encodage, donc si vous voulez utiliser autre chose que le mode binaire ou l'encodage par défaut, codecs.open était censé être utilisé.

Dans Python 2.6, le module io est venu en aide pour rendre les choses un peu plus simples. Selon la documentation officielle

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.

Cela dit, la seule utilisation à laquelle je peux penser codecs.opendans le scénario actuel est la compatibilité descendante. Dans tous les autres scénarios (sauf si vous utilisez Python <2.6), il est préférable d'utiliser io.open. Est également Python 3.x io.openle même quebuilt-in open

Remarque:

Il existe également une différence syntaxique entre codecs.openet 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
la source
Non seulement codecs.openet io.opendiffèrent en termes de syntaxe, ils renvoient des objets de type différent. Fonctionne également codecs.opentoujours avec des fichiers en mode binaire.
wombatonfire
4
  • Lorsque vous souhaitez charger un fichier binaire, utilisez f = io.open(filename, 'b').

  • Pour ouvrir un fichier texte, utilisez toujours f = io.open(filename, encoding='utf-8')un encodage explicite.

En python 3 fait cependant openla même chose io.openet peut être utilisé à la place.

Remarque: codecs.open est prévu de devenir obsolète et remplacé par io.openaprès son introduction dans python 2.6 . Je ne l'utiliserais que si le code doit être compatible avec les versions antérieures de python. Pour plus d'informations sur les codecs et l'unicode en python, consultez le HOWTO Unicode .

wihlke
la source
1. Pourquoi ne puis-je pas ouvrir un fichier en mode binaire avec io.openou codecs.open? 2. codecs.openn'est pas encore obsolète, lisez la discussion sur la page à laquelle vous avez lié.
wombatonfire
Bons points! 1. Vous pouvez utiliser l'un ou l'autre, mais je conseillerais à nouveau contre codecs.open à moins que vous ne soyez sur python 2.5 ou plus ancien. 2. J'ai mis à jour ma réponse pour indiquer que la dépréciation n'a pas eu lieu immédiatement, mais plutôt à l'avenir.
wihlke
3

Lorsque vous travaillez avec des fichiers texte et que vous souhaitez un encodage et un décodage transparents en objets Unicode.

Cat Plus Plus
la source
0

J'étais dans une situation pour ouvrir un fichier .asm et traiter le fichier.

#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:

Sans trop de problèmes, je suis capable de lire l'intégralité du fichier, des suggestions?

Gowtham Ch
la source