• en
  • Language: ru
  • Documentation version: latest

23. open Функция

open открывает файл. Довольно просто, да? Чаще всего мы видим, что она используется следующим образом:

f = open('photo.jpg', 'r+')
jpgdata = f.read()
f.close()

Причина, по которой я пишу эту статью, заключается в том, что чаще всего я вижу, как open используется подобным образом. В приведенном выше коде есть три ошибки. Можете ли вы заметить их все? Если нет, читайте дальше. К концу этой статьи вы будете знать, что не так в приведенном выше коде, и, что более важно, сможете избежать этих ошибок в своем собственном коде. Давайте начнем с основ:

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

Явный вызов close закрывает хэндл файла, но только если чтение было успешным. Если сразу после 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 к строке mode (если вы следуете примеру открытия, то правильным режимом будет rb). Если вы открываете что-то в текстовом режиме (т.е. добавляете t, или ничего, кроме r/r+/w/a), вы также должны знать, какую кодировку использовать. Для компьютера все файлы - это просто байты, а не символы.

К сожалению, open не позволяет явно указать кодировку в Python 2.x. Однако функция io.open доступна как в Python 2.x, так и в 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 = u'This is a JPEG file (%d bytes long)\n'
else:
    text = u'This is a random file (%d bytes long)\n'

with io.open('summary.txt', 'w', encoding='utf-8') as outf:
    outf.write(text % len(jpgdata))

Я уверен, что теперь вы будете правильно использовать open!