Перейти к содержанию

Введение в работу с файлами

Работа с файлами имеет большую связь с со многими внутренними частями операционных систем. Исторически был создан стандарт POSIX (читается как [позикс], как слово positive позитивный) — это целая семья стандартов того, как работают системные вызовы. Они описывают много вещей и среди прочего работу с файлами. Помимо POSIX еще есть Windows и другие операционные системы. Изначально в Python был взят за основу POSIX, а точнее реализация этого интерфейса в языке программирования Си. Постепенно он менялся для того, чтобы стать единым для всех поддерживаемых операционных систем. То есть интерфейс работы с файлами на самом деле это несколько библиотек для разных платформ, но вы работаете с ними через единый синтаксис, настолько насколько это возможно.

Особенности Windows

Поскольку работа с файлами все же связана с работой операционных систем, то нам придется обсуждать различия между ними. Если вы будете учиться работать с файлами в Windows и вам покажется, что это сложнее чем кажется, то ваши чувства вас не подводят. В POSIX-совместимых операционных системах таких как Ubuntu и другие Linux-ы или macOS, многие вещи гораздо удобнее и реже вызывают желание крушить или плакать в зависимости от вашего темперамента. Это нормально! К счастью, Python на самом деле сильно упрощает работу с файлами.

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

Работа с файлами в разных форматах — это самая большая часть стандартной библиотеки языка. Не получится перечислить все, но давайте назовем некоторые: os, os.path, pathlib, shutil, tempfile. Для работы с архивами zip, bz2, tar, gzip, lzma и еще другие. Для работ со структурированными файлами ini/cfg, csv, json, множеством форматов хранения электронных писем (Maildir, mbox, MH, Babyl, MMDF), для работы со звуковыми файлами и работа с файловыми дескрипторами в асинхронном режиме. Плюс еще создание файловых объектов в памяти или работа с файлами как с потоками. И это далеко не все.

Поэтому давайте начнем с основ, которые позволят вам получить верное представление о том, что такое файл с точки зрения компьютера и языка.

Что такое файл

На самом деле это не такой простой вопрос как кажется. Если у вас не было предварительного опыта программирования, то первое что придет в голову это объект, который лежит на диске в папке и который можно скопировать или удалить. Если кликнуть на него в проводнике, то откроется программа, которая умеет работать с этим файлом и в ней уже можно будет что-то отредактировать и изменить.

Даже если на основе этих небольших наблюдений собрать информацию вместе, то можно собрать следующие характеристики файлов:

  • У файла есть имя.
  • У файлов есть тип. Это часть имени файла после последней точки, например .txt.
  • У каждого файла есть путь. Файл лежит в какой-то директории (часто называют папкой), эта директория лежит в другой, а та на диске или сервере и т.д.

Расширение файла

В Windows тип файла обозначается иконкой, а расширение часто скрывается. В настройках проводника лучше всегда включать отображение расширения. Для программистов оно имеет гораздо большее значение чем для обычных пользователей. Специалисты по безопасности обоснованно критикуют практику скрытия расширения, ведь исполняемые файлы хранят иконку внутри себя, а значит файлу с расширением .exe можно добавить иконку безобидного текстового расширения и обманом заставить пользователя запустить вредоносную программу.

Если открыть картинку в редакторе текстов, то вместо графического представления вы получите кучу странных символов. Потому, что графическая информация упакована только для программ, которые могут расшифровывать специальный формат. Разные программы по-разному решают, как хранить данные внутри файлов. Но для многих подобных файлов есть стандарты, которые умеют распаковывать сразу много разных программ. Такие файлы называются бинарными, к ним относятся например все форматы картинок (png, jpeg, gif), видео (mp3, mp4, webp), на самом деле таких файлов слишком много даже для того, чтобы перечислить их типы.

Файлы, которые содержат только текст и которые можно открыть и редактировать без специальных редакторов называют текстовые. Все исходные коды на Python являются текстовыми файлами (расширение .py), кроме того, в Python проектах будут попадаться текстовые файлы с расширениями txt, md (markdown), rst (restructured text). Файлы, которые создаются в текстовых редакторах таких как Word на самом деле не являются текстовыми, они хранят информацию о тексте в бинарном виде, с ними работают с помощью специальных библиотек. Иногда случаются и непредвиденные ситуации, когда текстовый документ открывается в нечитаемом виде, обычно это значит, что они открыты с неправильной кодировкой. Мы начнем работать с текстовыми файлами, потому что их проще понимать без помощи специализированных программ и попытаемся разобраться с ними как они работают. Подразумевается, что файлы мы будем создавать в кодировке UTF-8, это стандартная кодировка строки для Python и по умолчанию эта кодировка используется в macOS и Linux.

Когда программа открывает файл, то обращается к операционной системе и в ответ получает специальный объект, который называется файловый хендлер или файловый дескриптор. Файловые хендлеры отвечают за ввод и вывод и передача данных между процессами. Даже для передачи данных по сети, что создает некоторую путаницу.

Стандартные файловые дескрипторы

В операционной системе всегда есть открытые хендлеры для ввода и вывода информации. Когда выполняется функция print, то она на самом деле отправляет данные в файловый хендлер stdout который всегда доступен всем программам. А функция input считывает информацию, поступающую из stdin, вот почему, когда мы работаем в интерактивном режиме с интерпретатором то traceback пишет, что ошибка в файле <stdin>:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
...

Кроме того, еще есть специальный файловый хендлер для вывода ошибок он называется stderr. Обычно программы публикуют текст для оператора в stdout, а ошибки в stderr и если программа запущена где-то на сервере, то сообщения об ошибках сохраняются в отдельный журнал. Чтобы потом можно было провести диагностику проще.

Если вы хотите попробовать поэкспериментировать с выводом, то можете указать функции print в какой файл выводить информацию через параметр file, а файл взять из модуля sys:

import sys
print("Это ошибка", file=sys.stderr)

image