Дисклеймер
Эта глава - полностью взята из Intermediate python / Функция open
Потому что кто-то опять закрыл интернет в учебных аудиториях и опять все материалы нужно размещать где-то локально.
С огромной благодарностью автору и переводчику. Ни на что не претендую и посылаю к первоисточнику или переводу, если у вас есть доступ к интернету. Читайте там. Там вычитка, исправления автором и добавления глав.
Функция open
open открывает файл. Логично, правда? Зачастую она используется следующим образом:
f = open('photo.jpg', 'r+')
jpgdata = f.read()
f.close()
Причина, по которой я пишу эту главу в том, что я очень часто вижу такой код. В нем три ошибки. Сможете найти? Если нет - продолжайте читать. В конце главы вы будете знать наверняка, что не так с кодом выше и, что важнее, сможете избегать подобных проблем в своем коде. Давайте начнем с основ.
open
возвращает дескриптор файла, полученный Python-приложением от вашей
операционной системы. Вам требуется вернуть дескриптор назад, после того как
работа с файлом завершена, иначе вы можете упереться в ограничение на
количество одновременно открытых дескрипторов.
Явно вызывая close
вы закрываете дескриптор файла, но только при успешном
чтении. При вызове Exception после f = open(...)
f.close()
не будет
выполнен (в зависимости от интерпретатора Python дескриптор может быть
возвращён, но это уже другая история). Чтобы быть уверенным в закрытии файла
вне зависимости от потенциальных ошибок необходимо использовать выражение
with
:
with open('photo.jpg', 'r+') as f:
jpgdata = f.read()
Первый аргумент open
это файл. Второй - (метод) определяет как файл
будет открыт.
- Если вы хотите прочесть файл -
r
- Для чтения и записи
r+
- Для перезаписи содержимого файла
w
- Добавление информации в файл
a
Существуют и другие методы, но вы их скорее всего никогда не встретите. Метод
важен не только из-за изменения поведения, но и поскольку он может привести к
ошибкам доступа. Например, если мы хотим открыть jpg файл в директории,
защищённой от записи, open(.., 'r+')
вызовет ошибку. Метод также может
содержать один дополнительный символ - мы можем открыть файл в бинарном виде
(вы получите строку байтов) или в текстовом (строка символов).
В целом, если формат написан людьми, то вам нужен текстовый метод. jpg
изображения не пишутся строка за строкой людьми (и не могут читаться ими),
поэтому их стоит открывать в бинарном виде, добавляя b
в метод
(если вы следуете примеру выше, то корректным будет rb
). Если вы
открываете что-то в текстовом формате (добавьте t
или ничего к
r/r+/w/a
) вы также должны знать кодировку. Для компьютера все файлы это
наборы байтов, не символов.
К сожалению, open
не позволяет непосредственно выбирать кодировку в
Python 2.x. Тем не менее, io.open
доступна в обоих ветках Python (в 3.x open
выступает в качестве алиаса)
и делает то, что нам нужно. Вы можете передавать кодировку в аргументе
encoding
. Если вы её не выберете, то система и Python остановятся на
кодировке по умолчанию. Вы можете попробовать довериться им, однако
стандартный выбор может быть полностью ошибочен или кодировка
не сможет отобразить часть символов в файле (такое часто происходит с
Python 2.x и Windows). Так что лучше выбирайте её самостоятельно.
utf-8
превосходна и поддерживается большинством браузеров и языков
программирования. При записи файла вы можете выбрать любую на свой вкус (или
отталкиваясь от предпочтений программы, которая будет этот файл читать).
Как определить кодировку файла, который вы пытаетесь прочесть? К сожалению, нет надежного способа определения правильной кодировки - одни и те же байты могут представлять различные, но разрешенные символы в различных кодировках. По этой причине вам придется опираться на метаданные (например, заголовки HTTP). Все чаще форматы определяют кодировку как UTF-8.
Вооруженные этими знаниями, давайте напишем программу, которая читает файл,
определяет является ли он JPG изображением (подсказка: эти файлы начинаются
с байтов FF D8
) и записывает текстовый файл, описывающий входной файл:
import io
with open('photo.jpg', 'rb') as inf:
jpgdata = inf.read()
if jpgdata.startswith(b'\xff\xd8'):
text = 'Это JPEG файл (%d байт)\n'
else:
text = 'Это произвольный файл (%d байт)\n'
with io.open('summary.txt', 'w', encoding='utf-8') as outf:
outf.write(text % len(jpgdata))
Теперь, я уверен, вы будете использовать open
правильно!