Слёрм Universe - Python на примере Minecraft
Работа с файлами
Лекция 9.1
Как правило, данные в программу поступают в текстовом виде. И важно понимать, что источников, откуда берётся этот текст, может быть множество. Один из таких важных источников - текстовый файл. Очень часто в программировании приходится считывать текстовые файлы, обрабатывать в них информацию, перезаписывать и так далее. Помните, на одной из практик мы уже работали с текстом из внешного источника? В нашем случае это были сообщения в чате Minecraft, на основе которых мы строили условие: ЕСЛИ написать необходимую фразу в чат, ТО возведётся арена. Мы точно также можем считывать текст в файле и на основе этого создавать условия. В Python файлы относятся к отдельному типу данных (да-да, именно к типу данных), который связывает переменную внутри программы с файлом, хранящимся на вашем компьютере.

Для начала давайте попробуем создать текстовый файл в PyCharm. Как вы помните, создавая файл для записи кода, мы указывали разрешение .py. Для того, чтобы PyCharm понимал, что в файле содержится обычный текст, создадим его привычным нам способом, но с разрешением .txt после имени. Например, player_names.txt - где мы перечислим с новой строки имена наших игроков:
Толстый Медведь
Хмурый Барсук
Лис
Кот
Фиолетовый Слон
Комар Пацифист
Далее создадим файл с любым именем и разрешением .py, куда мы, соответственно, будем записывать привычный нам код. Создадим в нём переменную player_names_file, которая и будет являться мостом между нашей программой и текстовым файлом:
player_names_file = open("player_names.txt")
Отлично, теперь у нас есть переменная, через которую можно производить обращение к файлу при помощи функции open(). Но как мы можем сделать это? Например, давайте попробуем вывести на экран содержимое нашего файла с текстом:
player_names_file = open("player_names.txt")
all_players = player_names_file.read()

print(all_players)
  • Мы создали ещё одну переменную all_players, в которую будет записан ВЕСЬ считанный текст из файла, содержащегося в переменной player_names_file. Для этого мы использовали метод read(), который ЧИТАЕТ все данные из файла и записывает их в СТРОКУ. Да-да, теперь в переменной all_players будет содержаться привычный нам тип данных - СТРОКА, а это значит, что теперь мы сможем к ней применять те же методы, функции, срезы и индексы, применимые к строкам;
  • Ну и в конце при помощи функции print() мы выводим СТРОКУ в переменной all_players, ранее полученную из файла, на экран:
Толстый Медведь
Хмурый Барсук
Лис
Кот
Фиолетовый Слон
Комар Пацифист
Супер, наш код работает! Мы считали все данные из файла при помощи функции read(). Но что делать, если нам потребуется не только читать из него, но и записывать? Давайте попробуем добавить ещё одно имя игрока в наш текстовый файл. Для этого воспользуемся функцией write():
player_names_file = open("player_names.txt")
player_names_file.write("\nЛетающий Кит")
  • Теперь мы не записываем переменную player_names_file с функцией write() в переменную, ведь мы не получаем данные, а наоборот, передаём их в файл;
  • А ещё обратите внимание на то, что в качестве аргумента функции write() мы указали перед строкой "Летающий Кит" символ перевода строки \n. То есть в таком случае "Летающий Кит" будет записан в текстовый файл с новой строки, а не впритык с предыдущим именем.

Запустим наш код:
Traceback (most recent call last):
  File "/Users/universe/PycharmProjects/SlurmUniverseProject/files.py", line 2, in <module>
    player_names_file.write("\nЛетающий Кит")
io.UnsupportedOperation: not writable
Упс, мы получили исключение not writable - то есть операция записи не поддерживается. Но как так? Почему мы можем записывать в файл руками, но не можем сделать это при помощи Python? Можем! Дело в том, что функция open() открывает по-умолчанию файл в режиме чтения, поэтому наша попытка записать в него не удалась. Передадим в open() ещё один аргумент, который укажет на то, что мы хотим именно записывать в файл, а не читать из него:
player_names_file = open("player_names.txt", "a")
player_names_file.write("\nЛетающий Кит")
Для этого после имени файла "player_names.txt" через запятую мы указали аргумент "a", благодаря которому файл открылся в режиме ДОЗАПИСИ. То есть после открытия файла курсор перейдёт в ЕГО КОНЕЦ и при вызове метода write() наша строка будет добавлена в самом конце. Теперь мы смогли добавить ещё одно имя игрока в текстовый файл. Давайте откроем его и посмотрим, что изменилось:
Толстый Медведь
Хмурый Барсук
Лис
Кот
Фиолетовый Слон
Комар Пацифист
Летающий Кит
Отлично! "Летающий Кит" был добавлен в конец с новой строки - то, что нужно.

Теперь попробуем в режиме дозаписи "a" прочитать содержимое файла, как мы делали это ранее, используя функцию read():
player_names_file = open("player_names.txt", "a")
player_names_file.write("\nЛетающий Кит")
all_players = player_names_file.read()

print(all_players)
То есть после дозаписи в файл ещё одного имени "Летающий Кит" мы вновь добавили переменную all_players и записали в неё вывод функции read(), а после выводим её содержимое на экран. Выполним код:
Traceback (most recent call last):
  File "/Users/universe/PycharmProjects/SlurmUniverseProject/files.py", line 3, in <module>
    all_players = player_names_file.read()
io.UnsupportedOperation: not readable
Ошибка! Теперь Python ругается на то, что файл не поддерживает чтение - not readable. Оно и понятно, ведь мы выбрали режим дозаписи, исключив возможность чтения из файла. То есть в один момент мы можем либо читать, либо записывать. Но что делать, если мы хотим сначала прочитать файл, а потом добавить в него данные? Кроме уже знакомых нам режимов чтения и дозаписи, существуют и другие режимы.

Рассмотрим пример, когда нам нужно СНАЧАЛА ПРОЧИТАТЬ файл, а ПОТОМ ИЗМЕНИТЬ его:
player_names_file = open("player_names.txt", "r+")
all_players = player_names_file.read()
print(all_players)

player_names_file.write("\nБык")
  • Обратите внимание, что мы использовали режим "r+" - режим чтения и дозаписи. Данный режим сначала считывает содержимое файла, а после перевод курсор в его конец и позволяет произвести дозапись в него;
  • Далее мы преобразовали содержимое файла в строку при помощи метода read() и записали её в переменную all_players;
  • Вывели полученную из файла строку на экран;
  • И в конце дозаписали в файл ещё одно имя игрока - "Бык".
Если выполнить этот код, то мы на экране отобразится содержимое файла ДО его дозаписи, а в конце самого файла появится ещё одна строчка с именем. Отлично!

Однако, часто бывает, что мы хотим не просто дополнить файл новым значением, а изменить его содержимое. И для этого существует ещё один режим. Давайте напишем программу, которая удаляет из файла player_names.txt имя "Хмурый Барсук":
player_names_file = open("player_names.txt")
all_players = player_names_file.read()
all_players = all_players.replace("Хмурый Барсук\n", "")
print(all_players)

player_names_file = open("player_names.txt", "w")
player_names_file.write(all_players)
  • В самом начале мы открываем файл player_names.txt в режиме по-умолчанию, то есть для чтения;
  • Далее при помощи функции read() мы записываем его содержимое в качестве строки в переменную all_players;
  • А после мы перезаписываем переменную all_players, используя строковый метод replace, в котором производим замену "Хмурый Барсук" на пустую строку, тем самым удаляя это имя из списка. Обратите внимание, что после удаляемого имени мы указали символ \n. Таким образом мы удаляем не только имя, но и перенос строки. Если этого не сделать, то после удаления в текстовом файле строчки "Хмурый Барсук" на её месте останется пустой интервал;
  • После мы выводим содержимое переменной all_player на экран при помощи print();
  • Обратите внимание, что после использования функции print(), мы снова открыли файл player_names.txt, но на этот раз в режиме "w". Что это за режим? В нём происходит ПЕРЕЗАПИСЬ всего файла. То есть содержимое текстового файла СНАЧАЛА ПОЛНОСТЬЮ ОЧИЩАЕТСЯ, А ПОСЛЕ В НЕГО ЗАПИСЫВАЮТСЯ НЕОБХОДИМЫЕ ДАННЫЕ при помощи уже знакомой нам функции write(). Кстати, если в этом режиме мы попробуем перезаписать файл, которого не существует, то он автоматически создастся, а после в него будет произведена запись;
  • В функцию write() мы передали строку с именами игроков, записанную в переменной all_players. Обратите внимание, что раннее из этой строки мы удалили имя "Хмурый Барсук".
Отлично! Если мы выполним код и откроем содержимое текстового файла:
Толстый Медведь
Лис
Кот
Фиолетовый Слон
Комар Пацифист
Летающий Кит
Бык
...то увидим, что в нём нет имени "Хмурый Барсук". Код сработал!

Как видите, мы рассмотрели несколько режимов работы с файлами:
  1. Чтение (по-умолчанию);
  2. Дозапись ("a");
  3. Чтение и дозапись ("r+");
  4. Перезапись ("w").
Однако, стоит отметить, что это далеко не все режимы. Для разных задач работы с файлами в Python используется разные режимы. Рассмотрим остальные:
Теперь мы умеем работать с текстовыми файлами - записывать в них данные и читать их содержимое. При помощи функции read() мы научились записывать в строку текст, полученные из файла, и присваивать её отдельным переменным. Это значит, что теперь такой текст - строка, к которому применимы функции и методы строк. Таким образом мы можем гибко изменять полученные данные из файла и записывать их обратно в файл.