Разница между open и codecs.open в Python


98

Есть два способа открыть текстовый файл в Python:

f = open(filename)

А также

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

Когда codecs.openпредпочтительнее open?


46
Обратите внимание, что codecs.open()это устарело в 3.x, так как open()получает encodingаргумент.
Игнасио Васкес-Абрамс,

Также существует третий способ (по крайней мере, в Python 2.x): `f = file (filename) '
Адам Паркин,

1
@ IgnacioVazquez-Abrams Есть ли codecs.open()устаревшие ссылки ? Я не думаю , что это в Python3 документы: docs.python.org/3.7/library/codecs.html
Варела

1
@varela: на упомянутой вами странице документации Python говорится: «Встроенный open () и связанный с ним модуль io - рекомендуемый подход для работы с закодированными текстовыми файлами»
Лучано Рамальо

Ответы:


83

Начиная с Python 2.6, хорошей практикой является использование io.open(), которое также принимает encodingаргумент, например, теперь устаревший codecs.open(). В Python 3 io.openэто псевдоним для open()встроенного. Так io.open()работает в Python 2.6 и всех более поздних версиях, включая Python 3.4. См. Документы: http://docs.python.org/3.4/library/io.html

Теперь, что касается исходного вопроса: при чтении текста (включая «обычный текст», HTML, XML и JSON) в Python 2 вы всегда должны использовать io.open()явную кодировку или open()явную кодировку в Python 3. Это означает, что вы получаете правильно декодировать Unicode или сразу получить сообщение об ошибке, что значительно упростит отладку.

Чистый ASCII «простой текст» - это миф из далекого прошлого. В правильном английском тексте используются фигурные кавычки, длинное тире, маркеры, € (знаки евро) и даже диэрезис (¨). Не будь наивным! (И давайте не будем забывать о шаблоне проектирования фасадов!)

Поскольку чистый ASCII не является реальным вариантом, open()без явного кодирования полезно только читать двоичные файлы.


5
@ForeverWintr Ответ здесь довольно ясен: использовать io.open()для текста и open()только для двоичных файлов . Подразумевается, что codecs.open()это вообще нежелательно.
Bdoserror

2
@Bdoserror, Существует ответ там, понятно, но это не ответ на вопрос , который был задан. Вопрос касался разницы между и и, в частности, когда последнее предпочтительнее первого. Ответ, который даже не упоминается, не может ответить на этот вопрос. opencodecs.opencodecs.open
ForeverWintr

3
@ForeverWintr Если OP задал неправильный вопрос (то есть с предположением, что codecs.open()его правильно использовать), тогда нет «правильного» ответа о том, когда его использовать. Ответ - использовать io.open()вместо этого. Это как если бы я спросил: «Когда я должен использовать гаечный ключ, чтобы вбить гвоздь в стену?». Правильный ответ - «используйте молоток».
Bdoserror 05

20

Лично я всегда использую, codecs.openесли нет четко определенной потребности в использовании open**. Причина в том, что очень много раз меня кусало, что входные данные utf-8 проникают в мои программы. «О, я просто знаю, что это всегда будет ascii» - это предположение, которое часто нарушается.

По моему опыту, использование utf-8 в качестве кодировки по умолчанию имеет тенденцию быть более безопасным выбором по умолчанию, поскольку ASCII можно рассматривать как UTF-8, но обратное неверно. И в тех случаях, когда я действительно знаю, что ввод - это ASCII, я все еще делаю это, codecs.openпоскольку твердо верю в «явное лучше, чем неявное» .

** - в Python 2.x, поскольку комментарий к вопросу в Python 3 openзаменяетcodecs.open


чего я действительно не понимаю, так это того, почему openиногда может очень хорошо обрабатывать нелатинские символы в кодировке UTF-8 из набора Unicode, а иногда он терпит неудачу ...
cedbeu

Для меня это имеет смысл. io.openне принимает параметр кодировки из того, что я вижу в python 2.7.5
radtek

1
@radtek, вы правы, что это недокументировано; Однако (по крайней мере в 2.7.12) io.openпринимает encodingи newlineпараметры и интерпретирует их как Python 3 делает. В отличие от этого codecs.open, файл, открытый с помощью, io.openбудет подниматься TypeError: write() argument 1 must be unicode, not strдаже в Python 2.7, если вы попытаетесь написать в него str( bytes). Файл, открытый с помощью codecs.open, вместо этого будет пытаться неявно преобразовать в unicode, что часто приводит к путанице UnicodeDecodeError.
jochietoch

9

В Python 2 есть строки Unicode и байтовые строки. Если вы просто используете байтовые строки, вы можете читать / писать в файл, открытый без проблем open(). В конце концов, строки - это просто байты.

Проблема возникает, когда, скажем, у вас есть строка Unicode, и вы делаете следующее:

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

Итак, здесь, очевидно, вы либо явно кодируете свою строку Unicode в utf-8, либо используете, codecs.openчтобы сделать это прозрачно.

Если вы когда-либо используете только байтовые строки, проблем нет:

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

Это становится более сложным, чем это, потому что, когда вы объединяете строку Unicode и строку байтов с +оператором, вы получаете строку Unicode. Легко быть укушенным этим.

Также codecs.openне любит байтовые строки с не-ASCII символами, передаваемыми в:

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)

Совет относительно строк для ввода / вывода обычно таков: «преобразовать в Unicode как можно раньше, а обратно в байтовые строки как можно позже». Использование codecs.openпозволяет очень легко сделать последнее.

Просто будьте осторожны, дайте ему строки Unicode, а не строки байтов, которые могут содержать символы, отличные от ASCII.


Вы можете объяснить свой второй пример? Кажется, он идентичен вашему первому примеру, так почему же результат должен быть другим?
Крис Джонсон,

Обратите внимание на использование u''в первом примере. Это означает, что я создал строку юникода, а не строку байтов. В этом разница между двумя примерами. Во втором примере я создаю байтовую строку, и записать одну из них в файл вполне нормально. Строка Unicode не подходит, если вы используете символы вне ASCII.
Mandible79

7

Когда вам нужно открыть файл с определенной кодировкой, вы должны использовать codecsмодуль.


15
Я думаю, что все текстовые файлы каким-то образом имеют определенную кодировку (:
cedbeu

5

codecs.openЯ полагаю, это всего лишь пережиток тех Python 2дней, когда встроенный open имел гораздо более простой интерфейс и меньше возможностей. В Python 2 встроенный openне принимает аргумент кодировки, поэтому, если вы хотите использовать что-то другое, кроме двоичного режима или кодировки по умолчанию, предполагалось использовать codecs.open.

В In Python 2.6, модуль io пришел на помощь, чтобы упростить задачу. Согласно официальной документации

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.

Сказав это, единственное использование, которое я могу придумать codecs.openв текущем сценарии, - это обратная совместимость. Во всех других сценариях (если вы не используете Python <2.6) предпочтительнее использовать io.open. Также в Python 3.x io.openтакой же, какbuilt-in open

Заметка:

Существует синтаксическая разница между codecs.openи 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)

Мало того, codecs.openи io.openразличаются с точки зрения синтаксиса, они возвращают объекты различного типа. Также codecs.openвсегда работает с файлами в двоичном режиме.
wombatonfire

4
  • Если вы хотите загрузить двоичный файл, используйте f = io.open(filename, 'b').

  • Для открытия текстового файла всегда используйте f = io.open(filename, encoding='utf-8')явную кодировку.

В Python 3 , однако openделает то же самое, что io.openи может быть использован вместо.

Примечание. codecs.open Планируется, что после его появления в Python 2.6 он станет устаревшим и будет заменен на . Я бы использовал его только в том случае, если код должен быть совместим с более ранними версиями python. Для получения дополнительной информации о кодеках и Unicode в Python см. Unicode HOWTO .io.open


1. Почему я не могу открыть файл в двоичном режиме с помощью io.openили codecs.open? 2. codecs.openеще не устарел, прочтите обсуждение на странице, на которую вы ссылаетесь.
wombatonfire

Хорошие моменты! 1. Вы можете использовать любой из них, но я бы снова посоветовал использовать codecs.open, если вы не используете python 2.5 или старше. 2. Я обновил свой ответ, чтобы отразить, что прекращение поддержки произошло не сразу, а скорее в будущем.
wihlke

3

Когда вы работаете с текстовыми файлами и хотите прозрачное кодирование и декодирование в объекты Unicode.


0

Я был в ситуации, чтобы открыть файл .asm и обработать его.

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

Без особых проблем я могу прочитать весь файл, есть предложения?

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.