Різниця між open і codecs.open у Python


95

Існує два способи відкрити текстовий файл у Python:

f = open(filename)

І

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

Коли codecs.openкраще open?


45
Зверніть увагу, що codecs.open()це застаріло в 3.x, оскільки open()отримує encodingаргумент.
Ігнасіо Васкес-Абрамс

Існує також 3-й спосіб (принаймні в Python 2.x): `f = файл (ім'я файлу) '
Адам Паркін,

1
@ IgnacioVazquez-Abrams Чи існує codecs.open()застаріле посилання ? Я не думаю, що це в python3 docs: docs.python.org/3.7/library/codecs.html
varela,

1
@varela: на зазначеній вами сторінці документації Python сказано: "вбудований open () та пов'язаний з ним модуль io є рекомендованим підходом для роботи з закодованими текстовими файлами"
Luciano Ramalho,

Відповіді:


82

Починаючи з 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 Якщо ОП задало неправильне запитання (тобто з припущенням, що codecs.open()було правильним для використання), тоді немає "правильної" відповіді про те, коли його використовувати. Відповідь - використовувати io.open()замість цього. Це як якщо я запитую "коли мені слід за допомогою ключа забивати цвях у стіну?". Правильна відповідь - "використовуйте молоток".
Bdoserror

20

Особисто я завжди користуюся, codecs.openякщо немає чітко визначеної потреби використовувати open**. Причина полягає в тому, що так багато разів мене кусало, коли в мої програми проникав вхід utf-8. "О, я просто знаю, що це буде завжди ascii", як правило, є припущенням, яке часто порушується.

Припускаючи, що 'utf-8' як кодування за замовчуванням, як правило, є більш безпечним вибором за замовчуванням, оскільки ASCII можна розглядати як UTF-8, але зворотне не відповідає дійсності. І в тих випадках, коли я справді знаю, що вхідні дані - це ASCII, тоді я все одно роблю те codecs.open, що я твердо вірю в "явне краще, ніж неявне" .

** - у Python 2.x, як openзамінено коментарем до питання в Python 3codecs.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.openwill, замість цього робить спробу неявного перетворення unicode, часто приводячи до плутанини UnicodeDecodeErrors.
jochietoch 02

9

У Python 2 є рядки Unicode і bytestring. Якщо ви просто використовуєте bytestring, ви можете читати / писати у файл, відкритий 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)

Отже, тут, очевидно, ви або явно кодуєте свій рядок унікоду в utf-8, або використовуєте, codecs.openщоб зробити це для вас прозоро.

Якщо ви тільки коли-небудь використовуєте bytestrings, то проблем немає:

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

Він залучається більше, ніж це, тому що, коли ви об'єднуєте рядок unicode та bytestring з +оператором, ви отримуєте рядок unicode. Його легко вкусити.

Також codecs.openне подобається bytestring з символами, що не належать до 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 якомога раніше і повертати в bytestring якомога пізніше". Використання codecs.openдозволяє зробити останнє дуже легко.

Тільки будьте обережні, що ви надаєте йому рядки Unicode, а не bytestring, які можуть мати символи, що не є ASCII.


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

Зверніть увагу на використання u''в першому прикладі. Це означає, що я створив рядок юнікоду, а не байтовий рядок. У цьому різниця між двома прикладами. У другому прикладі я створюю тестування байтів і виписую одне з них у файл - це чудово. Рядок Unicode не є нормальним, якщо ви використовуєте символи поза ASCII.
Mandible79

7

Коли вам потрібно відкрити файл, який має певне кодування, ви використовуєте codecsмодуль.


15
Я думаю, усі текстові файли мають певне кодування, так чи інакше (:
cedbeu

5

codecs.open, я припускаю, це лише залишок з тих Python 2часів, коли вбудований open мав набагато простіший інтерфейс і менше можливостей. У Python 2 вбудований openне приймає аргументу кодування, тому, якщо ви хочете використовувати щось інше, ніж двійковий режим або кодування за замовчуванням, передбачалося використовувати codecs.open.

У 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 планується застаріти та замінити io.openпісля введення в python 2.6 . Я б використовував його, лише якщо код повинен бути сумісним з попередніми версіями python. Для отримання додаткової інформації про кодеки та unicode в python див. Unicode HOWTO .


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

Хороші бали! 1. Ви можете використовувати будь-який, але я б знову порадив проти кодеків.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.