Все переменные, которые вы создаете, исчезнут после закрытия программы – так работает временная память устройства. Если вы хотите сохранить какую-то информацию на неопределенный срок – вам нужно записать ее в постоянное хранилище, на жесткий диск. Контейнеры для хранения информации на жестком диске – файлы, и Python предоставляет ряд встроенных методов для работы с ними.
Файл – это внешний ресурс, и ваше приложение – не единственное, которое хочет пользоваться внешними ресурсами. Когда дело касается памяти – операционная система может выдать каждому приложению свою «лужайку», на которой приложение делает все, что хочет; с файлами – сложнее, часто случается так, что одно приложение читает информацию, которую в файл помещает другое приложение. И это уже представляет проблему: если одно приложение пишет, а другое в это же время читает, то как читающий может быть уверен, что он читает свежую информацию. Все становится еще хуже, если 2 приложения одновременно пишут данные в файл: на выходе получится мешанина из двух разных потоков данных.
Чтобы решить все эти проблемы, операционная система устанавливает правило: одним конкретным файлом в конкретный момент времени может пользоваться только одно приложение. Если ваш «Hello world!» открыл файл для записи или чтения – ни одно другое приложение не может открыть этот же файл, пока вы его не закроете. Поэтому запомните:
Если вы будете держать эти принципы в голове, работать с файлами в Python (как и в любом другом ЯП) вам будет куда проще.
Итак, сначала научимся самому главному – открывать и закрывать файлы в Python. Открыть файл можно с помощью встроенной функции open(), сигнатура:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
Обязательный параметр – только первый, file, путь к файлу. Функция возвращает файловый объект, с которым можно работать через методы. По опциональным параметрам – mode рассмотрим отдельно, вкратце об остальных:
Если вам вдруг нужно узнать больше о каком-то из этих параметров – смотрите официальную документацию.
Что касается необязательного параметра mode – с его помощью можно задать режим, в котором будет открыт файл: для чтения, для записи, текстовый, бинарный и так далее. Проще всего запомнить режимы по группам:
Режим |
Описание |
С каким файлом работаем? |
|
t |
Текстовый файл. Является режимом по умолчанию – если работаем с текстом, то “t” писать не надо, мы рассказываем про него только для того, чтобы вы знали о его существовании |
b |
Бинарный файл. По суть – набор байт. Может быть текстом, картинкой, набором структур и вообще чем угодно. “b” нужно указывать, если “b” нет – Python считает, что мы работаем с текстовым файлом. |
Хотим создать, прочитать ИЛИ записать |
|
r |
Читать данные из файла. Если файла не существует – получаем исключение |
w |
Записать данные в файл. Если файла не существует – он будет создан. Если файл уже существует и хранит в себе какую-то информацию – она будет удалена(!) |
x |
Создать файл. Если файл с таким именем уже существует – получаем исключение |
a |
Записать данные в конец файла. Если он уже существует – данные будут записаны после тех, что уже хранятся в файле. Если он не существует – он будет создан |
Хотим прочитать И записать |
|
r+ |
Открывает файл для чтения и записи, если файла с таким именем нет – генерирует исключение |
w+ |
Открывает файл для чтения и записи, если файл уже существует – перезаписывает его |
a+ |
Открывает файл для чтения и записи, если файл уже существует – записывает данные в конец файла |
Итак, для работы с файлом вам нужно открыть его, выбрав нужный режим (или открыв в стандартном режиме – tr). После того, как вы с ним поработаете, файл нужно будет закрыть через .close(). Синтаксис будет выглядеть так:
file = open("example.txt", "w")
file.write("Hello, world!")
file.close()
Но тут есть проблема – file.write() может генерировать исключения, и если это случится – file.close() не будет исполнен. Для того, чтобы избежать этого, нужно использовать диспетчер контекста with – он автоматически закроет файл, когда поток исполнения выйдет из блока:
with open("example.txt", "w") as file:
file.write("Hello, world!")
Такое открытие файла считается хорошей практикой.
Для того, чтобы прочитать данные из файла, нужно воспользоваться методом .read(). Если в него передать число в качестве аргумента, метод считает указанное количество байт; если не передавать число, метод поместит в переменную весь файл:
with open("example.txt", "r", encoding="utf-8") as file:
data = file.read()
print(data)
Прочитать следующую строчку можно с помощью метода .readline(), а если вы хотите прочитать сразу все строки – метод .readlines() вернет вам список строк:
with open("example.txt", "r", encoding="utf-8") as file:
lines = file.readlines()
for line in lines:
print(line.strip())
В целом предпочтительно читать файлы по строкам, потому что если вы случайно прикажете интерпретатору считать файл весом 2 Гб в переменную – он это сделает.
С записью все так же, только вместо .read() мы используем .write() (или .writelines(), которому передаем список строк):
with open("example.txt", "w", encoding="utf-8") as file:
file.write("Hello, world!\n")
file.write("Привет, мир!\n")
Не забывайте добавлять символ перевода строки в конце каждой – .write() этого не делает.
Еще один способ записи в файл – стандартный метод print(), которому в качестве аргумента передают имя файла:
with open("example.txt", "a", encoding="utf-8") as file:
print("Hello, sravni.ru", file=file)
Все эти методы чтения и записи позволяют читать/записывать данные символ за символом, строка за строкой. За последовательность отвечает курсор – после чтения/записи каждого нового символа он смещается. Вы можете управлять курсором с помощью функции .seek():
with open("example.txt", "w+", encoding="utf-8") as file:
file.write("Hello, world!\n")
file.write("Привет, мир!\n")
file.seek(0)
content = file.read()
print(content)
file.seek(7)
content = file.read()
print(content)
Здесь мы записали данные, вернулись в начало файла, считали весь файл и вывели его, переместились на 7-й байт, считали все от 7 байта до конца файла и снова вывели содержимое. Главное – помните, что .seek() считает байты, а не символы – это может оказаться проблемой, если кодировка текста выделяет больше 1 байта на символ.
Python предоставляет несколько библиотек для работы с файловой системой, базовые манипуляции можно найти в разделе «Files and directories» модуля os, для сложных операций с файлами есть модуль shutil. Разбор этой сложной темы выходит за рамки данного материала, поэтому просто вкратце перечислим интересные методы – больше информации о них вы найдете в документации:
Тезисно: