Diferença entre open e codecs.open em Python

93

Existem duas maneiras de abrir um arquivo de texto em Python:

f = open(filename)

E

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

Quando é codecs.openpreferível open?

BlogueroConnor
fonte
44
Observe que codecs.open()está obsoleto no 3.x, pois open()ganha um encodingargumento.
Ignacio Vazquez-Abrams,
Há também uma terceira forma (no Python 2.x, pelo menos): `f = arquivo (nome do arquivo) '
Adam Park em
1
@ IgnacioVazquez-Abrams Existe algum link que codecs.open()está obsoleto? Não acho isso nos documentos python3
varela
1
@varela: a página de documentação do Python que você mencionou diz: "o open () integrado e o módulo io associado são a abordagem recomendada para trabalhar com arquivos de texto codificados"
Luciano Ramalho

Respostas:

82

Desde o Python 2.6, uma boa prática é usar io.open(), que também leva um encodingargumento, como o now obsolete codecs.open(). No Python 3, io.opené um alias para o open()integrado. Portanto, io.open()funciona no Python 2.6 e em todas as versões posteriores, incluindo Python 3.4. Veja os documentos: http://docs.python.org/3.4/library/io.html

Agora, para a pergunta original: ao ler texto (incluindo "texto simples", HTML, XML e JSON) em Python 2, você deve sempre usar io.open()com uma codificação explícita ou open()com uma codificação explícita em Python 3. Isso significa que você acertou decodificar Unicode ou obter um erro logo de cara, tornando-o muito mais fácil de depurar.

O "texto simples" em ASCII puro é um mito do passado distante. O texto em inglês apropriado usa aspas curvas, travessões, marcadores, € (sinais de euro) e até mesmo diérese (¨). Não seja ingênuo! (E não vamos esquecer o padrão de design da fachada!)

Como o ASCII puro não é uma opção real, open()sem uma codificação explícita é útil para ler arquivos binários .

Luciano Ramalho
fonte
5
@ForeverWintr A resposta está bem clara aí: use io.open()para texto e open()apenas para binário. A implicação é que codecs.open()não é de todo preferido.
Bdoserror
2
@Bdoserror, há uma resposta aí, claramente, mas não é uma resposta para a pergunta que foi feita. A questão era sobre a diferença entre opene e codecs.open, especificamente, quando o último é preferível ao primeiro. Uma resposta que nem mesmo menciona codecs.opennão pode responder a essa pergunta.
ForeverWintr
3
@ForeverWintr Se o OP fez a pergunta errada (ou seja, com a suposição de que codecs.open()era correto usar), então não há uma resposta "correta" sobre quando usá-lo. A resposta é usar em seu io.open()lugar. É como se eu perguntasse "quando devo usar uma chave inglesa para cravar um prego na parede?". A resposta certa é "use um martelo".
Bdoserror,
20

Pessoalmente, eu sempre uso, a codecs.openmenos que haja uma necessidade claramente identificada de uso open**. A razão é que muitas vezes eu fui mordido por uma entrada de utf-8 furtivamente em meus programas. "Oh, eu só sei que sempre será ascii" tende a ser uma suposição que é quebrada com frequência.

Presumir 'utf-8' como a codificação padrão tende a ser uma escolha padrão mais segura em minha experiência, já que ASCII pode ser tratado como UTF-8, mas o inverso não é verdade. E, nos casos em que realmente sei que a entrada é ASCII, ainda acredito, codecs.openpois acredito firmemente que "explícito é melhor do que implícito" .

** - no Python 2.x, pois o comentário sobre a pergunta afirma no Python 3 opensubstituicodecs.open

Adam Parkin
fonte
o que eu realmente não entendo é por que openàs vezes consegue lidar muito bem com caracteres não latinos codificados em UTF-8 do conjunto Unicode, e às vezes falha miseravelmente ...
cedbeu
Isso faz sentido para mim. io.opennão leva um parâmetro de codificação do que posso ver em python 2.7.5
radtek
1
@radtek, você está certo ao dizer que isso não é documentado; entretanto (pelo menos em 2.7.12) io.openaceita encodinge newlineparâmetros e os interpreta como o Python 3 faz. Ao contrário codecs.open, um arquivo aberto com io.openaumentará TypeError: write() argument 1 must be unicode, not straté mesmo no Python 2.7 se você tentar escrever str( bytes) nele. Um arquivo aberto com codecs.opententará, em vez disso unicode, a conversão implícita para , geralmente levando a arquivos confusos UnicodeDecodeError.
jochietoch,
9

No Python 2, existem strings Unicode e bytestrings. Se você apenas usar bytestrings, poderá ler / gravar em um arquivo aberto com open()muito bem. Afinal, as strings são apenas bytes.

O problema surge quando, digamos, você tem uma string Unicode e faz o seguinte:

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

Então aqui obviamente você codifica explicitamente sua string Unicode em utf-8 ou usa codecs.openpara fazer isso de forma transparente.

Se você sempre usa bytestrings, não há problemas:

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

É mais complicado do que isso porque quando você concatena uma string Unicode e uma string de bytes com o +operador, você obtém uma string Unicode. Fácil de ser mordido por aquele.

Também codecs.opennão gosta de bytes com caracteres não ASCII sendo passados ​​em:

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)

O conselho sobre strings para entrada / saída é normalmente "converta para Unicode o mais cedo possível e de volta para bytestrings o mais tarde possível". Usar codecs.openpermite que você faça o último com muita facilidade.

Apenas tome cuidado para não fornecer strings Unicode e não bytes que possam conter caracteres não ASCII.

Mandíbula 79
fonte
Você pode explicar seu segundo exemplo? Parece ser idêntico ao seu primeiro exemplo, então por que o resultado seria diferente?
Chris Johnson
Observe o uso do u''no primeiro exemplo. Isso significa que criei uma string Unicode, não uma bytestring. Essa é a diferença entre os dois exemplos. No segundo exemplo, estou criando um bytestring e escrever um deles em um arquivo está ótimo. Uma string Unicode não funciona se você estiver usando caracteres fora de ASCII.
Mandible79 de
7

Quando você precisa abrir um arquivo que possui uma determinada codificação, você deve usar o codecsmódulo.

Geo
fonte
15
Acho que todos os arquivos de texto têm uma certa codificação, de alguma forma (:
cedbeu
5

codecs.open, suponho, é apenas um resquício da Python 2época em que o open embutido tinha uma interface muito mais simples e menos recursos. No Python 2, embutido opennão aceita um argumento de codificação, então se você quiser usar algo diferente do modo binário ou a codificação padrão, codecs.open deveria ser usado.

Em Python 2.6, o módulo io veio em ajuda para tornar as coisas um pouco mais simples. De acordo com a documentação oficial

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.

Dito isso, o único uso que posso pensar codecs.openno cenário atual é para a compatibilidade com versões anteriores. Em todos os outros cenários (a menos que você esteja usando Python <2.6), é preferível usar io.open. Também em Python 3.x io.opené o mesmo quebuilt-in open

Nota:

Há uma diferença sintática entre codecs.opene io.opentambém.

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)
aprender
fonte
Não só codecs.opene io.opendiferem em termos de sintaxe, eles retornam objetos do tipo diferente. Também codecs.opensempre funciona com arquivos em modo binário.
wombatonfire
4
  • Quando você quiser carregar um arquivo binário, use f = io.open(filename, 'b').

  • Para abrir um arquivo de texto, use sempre f = io.open(filename, encoding='utf-8')com codificação explícita.

No entanto, em python 3open faz a mesma coisa io.opene pode ser usado em seu lugar.

Nota: codecs.open está planejado para se tornar obsoleto e substituído por io.openapós sua introdução no python 2.6 . Eu só o usaria se o código precisar ser compatível com versões anteriores do python. Para mais informações sobre codecs e unicode em python, veja o Unicode HOWTO .

wihlke
fonte
1. Por que não consigo abrir um arquivo no modo binário com io.openou codecs.open? 2. codecs.openainda não está obsoleto, leia a discussão na página que você vinculou.
wombatonfire
Bons pontos! 1. Você pode usar qualquer um, mas eu recomendaria novamente contra codecs.open, a menos que você esteja no python 2.5 ou mais antigo. 2. Atualizei minha resposta para refletir que a suspensão de uso não ocorreu imediatamente, mas sim no futuro.
wihlke
3

Quando você está trabalhando com arquivos de texto e deseja codificação e decodificação transparentes em objetos Unicode.

Cat Plus Plus
fonte
0

Eu estava em uma situação de abrir um arquivo .asm e processar o arquivo.

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

Sem muita dificuldade consigo ler todo o arquivo, alguma sugestão?

Gowtham Ch
fonte