Модель объекта файла в Python 3.X
Как и в случае со строковыми типами, о которых говорилось в главе 2, поддержка файлов в Python 3.X стала гораздо богаче, чем в предыдущих версиях. Как уже отмечалось ранее, в Python 3.X строки типа str всегда представляют текст Юникода (символы ASCII или многобайтовые символы), а строки типов bytes и bytearray представляют простые двоичные данные. Python 3.X проводит подобные различия между файлами, содержащими текст и двоичные данные:
• Текстовые файлы, содержат текст, состоящий из символов Юникода. Содержимое текстовых файлов в сценариях всегда представляется в виде строк типа str - последовательностей символов (точнее, последовательностей «кодовых пунктов» Юникода). Для текстовых файлов автоматически выполняется преобразование символов конца строки, о котором рассказывается в этой главе, а к содержимому файлов автоматически применяются операции кодирования/деко-дирования: данные кодируются в двоичное представление при записи в файл и декодируются обратно в Юникод при чтении из файла, в соответствии с указанной или используемой по умолчанию кодировкой. Кодирование является тривиальной операцией для текста ASCII, но может быть весьма сложной в других случаях.
• Двоичные файлы содержат обычные 8-битовые байты. Содержимое двоичных файлов в сценариях всегда представляется в виде строк байтов, обычно в виде объекта типа bytes - последовательности коротких целых чисел, которые поддерживают большинство операций, присущих типу str, и отображаются как последовательности символов ASCII, когда это возможно. Для двоичных файлов не предусматривается никаких преобразований данных при чтении или записи: ни преобразования символов конца строки, ни кодирования/ декодирования в Юникод.
На практике текстовые файлы используются для хранения действительно текстовых данных, а двоичные файлы - для хранения таких элементов, как упакованные двоичные данные, изображения, аудиоданные, выполняемый программный код и так далее. Программно эти два типа файлов различаются с помощью аргумента со строкой режима, который передается функции open: дополнительный символ «Ь» (например, ‘rb’, ‘wb’) означает, что файл содержит двоичные данные. Для создания нового содержимого текстовых файлов используются обычные строки (например, ‘spam’ или bytes.decode()), а для создания нового содержимого двоичных файлов - строки байтов (например, bspam’ или str.encode()).
Если в вашей практике область применения файлов не ограничивается использованием текста в кодировке ASCII, различия между представлением текстовых и двоичных данных в версии 3.X иногда будут сказываться на вашем программном коде. При работе с текстовыми файлами требуется использовать строки типа str, а с двоичными файлами - строки байтов. Поскольку вы не сможете смешивать эти типы в выражениях, вам придется внимательно подходить к вопросу выбора режима открытия файла. Многие встроенные инструменты, которые мы будем использовать в этой книге, делают этот выбор за нас - модули struct и pickle, например, в версии 3.X работают со строками байтов, а пакет xml - с Юникодом. Кроме того, о различиях между текстовыми и двоичными данными в версии 3.X необходимо помнить даже при использовании системных инструментов, таких как дескрипторы каналов и сокеты, потому что на сегодняшний день эти инструменты передают данные в виде строк байтов (впрочем, при необходимости эти данные можно кодировать и декодировать как текст Юникода).
Кроме того, при работе с текстовыми файлами выполняется обязательное декодирование их содержимого в Юникод в соответствии с выбранной кодировкой, поэтому вам придется использовать двоичный режим для чтения содержимого файлов, не поддающихся декодированию, в виде строк байтов (или обрабатывать исключения декодирования в Юникод с помощью инструкций try и пропускать такой файл целиком). Это относится и к собственно двоичным файлам, и к текстовым файлам, для представления текста в которых используется неподдерживаемая или неизвестная кодировка. Как мы увидим далее в этой главе, в версии 3.X строки типа str всегда содержат текст Юникода, поэтому иногда придется использовать строки байтов для представления имен файлов при использовании таких инструментов, как os.listdir, glob.glob и os.walk, если они не могут быть декодированы (передача в виде строки байтов фактически подавляет необходимость декодирования).