# Функция open

[open](http://docs.python.org/dev/library/functions.html#open) открывает файл. Логично, правда? Зачастую она используется следующим образом:

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

Причина, по которой я пишу эту главу в том, что я очень часто вижу такой код. В нем **три** ошибки. Сможете найти? Если нет - продолжайте читать. В конце главы вы будете знать наверняка, что не так с кодом выше и, что важнее, сможете избегать подобных проблем в своем коде. Давайте начнем с основ.

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

Явно вызывая `close` вы закрываете дескриптор файла, но только при успешном чтении. При вызове Exception после `f = open(...)` `f.close()` не будет выполнен (в зависимости от интерпретатора Python дескриптор может быть возвращён, но это уже другая история). Чтобы быть уверенным в закрытии файла вне зависимости от потенциальных ошибок необходимо использовать выражение `with`:

```python
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](http://docs.python.org/2/library/io.html#io.open) доступна в обоих ветках Python (в 3.x `open` выступает в качестве алиаса) и делает то, что нам нужно. Вы можете передавать кодировку в аргументе `encoding`. Если вы её не выберете, то система и Python остановятся на кодировке по умолчанию. Вы можете попробовать довериться им, однако стандартный выбор может быть полностью ошибочен или кодировка не сможет отобразить часть символов в файле (такое часто происходит с Python 2.x и Windows). Так что лучше выбирайте её самостоятельно. `utf-8` превосходна и поддерживается большинством браузеров и языков программирования. При записи файла вы можете выбрать любую на свой вкус (или отталкиваясь от предпочтений программы, которая будет этот файл читать).

Как определить кодировку файла, который вы пытаетесь прочесть? К сожалению, нет надежного способа определения правильной кодировки - одни и те же байты могут представлять различные, но разрешенные символы в различных кодировках. По этой причине вам придется опираться на метаданные (например, заголовки HTTP). Все чаще форматы определяют кодировку как UTF-8.

Вооруженные этими знаниями, давайте напишем программу, которая читает файл, определяет является ли он JPG изображением (подсказка: эти файлы начинаются с байтов `FF D8`) и записывает текстовый файл, описывающий входной файл:

```python
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` правильно!
