4.1. Работа с файлами
Информатика, 7 класс. Учебник: Кадыркулов Р. А., Рыскулбекова А. Д., Нурмуханбетова Г. К. (Алматыкітап баспасы, 2023). Раздел 4: Программирование алгоритмов на языке Python. Страницы 105–110.
Чему научишься
- Объяснять, зачем программе файлы, если есть клавиатура и экран.
- Открывать файл функцией
open()и выбирать режим:'r','w','a'. - Читать данные из файла методом
read()и записывать методомwrite(). - Всегда закрывать файл командой
close()и понимать, что будет, если забыть. - Писать программу по схеме «прочитать из входного файла, посчитать, записать в выходной».
Разминка
- Как ты вводил данные в программах на Python? С клавиатуры, функцией
input(). Результат программа показывала на экране черезprint(). - Знаешь ли ты другие способы получить значения для переменных? Да, их можно прочитать из файла. Ещё значения бывают заданы прямо в тексте программы, например
a = 5.
Разбираемся
Зачем программе файл
Представь, что ты делаешь электронный справочник одноклассников: 30 фамилий, 30 телефонов. Вводить всё с клавиатуры при каждом запуске замучаешься. А если справочник на 500 записей, это вообще нереально.
Решение: один раз записать данные в текстовый файл, а программа сама откроет его и найдёт нужное. Любая программа делает три дела: ввод, обработка, вывод. Файл помогает и с вводом, и с выводом.
💡 Файл это как тетрадь: данные в ней лежат, даже когда компьютер выключен. А переменные в программе пропадают, как только она закончилась.
Файлы бывают двух видов.
- Текстовые хранят буквы и цифры. С ними мы работаем в 7 классе.
- Двоичные (бинарные) хранят картинки, звук, видео в виде двоичного кода. С ними познакомишься в старших классах.
Три шага: открыл, поработал, закрыл
С файлом всегда одно и то же. Сначала его надо открыть, потом прочитать или записать данные, а в конце закрыть, чтобы освободить ресурсы компьютера.
Вот вся программа целиком:
f = open("hello.txt", "w") # открыли файл для записи
f.write("Hello, file!") # записали строку
f.close() # закрыли
Запусти её в IDLE или Thonny (программу сохрани в отдельную папку). Рядом с ней появится файл hello.txt. Если такой файл уже был, программа не создаёт новый, а работает со старым.
⚠️ Частая ошибка. Пишешь русский текст в файл, а потом видишь «кракозябры». Тогда укажи кодировку:
open("x.txt", "w", encoding="utf-8"). В примерах урока текст английский, так проще.
Режимы: что ты хочешь от файла
Второй параметр open() это режим. Он говорит Питону, что ты собираешься делать.
| Режим | Что делает |
|---|---|
'r' |
открыть, чтобы только читать (так по умолчанию) |
'w' |
создать файл и записывать. Если файл был, старое содержимое стирается |
'a' |
открыть старый файл и дописывать в конец |
'b' |
двоичный режим |
't' |
текстовый режим (по умолчанию) |
'+' |
и читать, и записывать |
⚠️ Частая ошибка. Открыл готовый файл в режиме
'w'и «потерял» все данные. Режим'w'стирает старое в ту же секунду. Если нужно добавить, бери'a'.
Вот что происходит с файлом log.txt, если его три раза открыть по-разному.
| Что сделали | Что внутри файла |
|---|---|
open("log.txt", "w"), записали one |
one |
open("log.txt", "a"), дописали two |
one и two в конце |
снова open("log.txt", "w"), записали three |
только three, старое стёрто |
Читаем из файла, считаем, пишем в другой
Типичная задача: во входном файле два числа через пробел, нужно записать в выходной файл их сумму и произведение. Возьмём свои числа: в input.txt лежит 8 5.
fin = open("input.txt", "r") # входной файл, читаем
a, b = map(int, fin.read().split()) # две строки-числа превратили в числа
fin.close() # прочитали, закрываем
fout = open("output.txt", "w") # выходной файл, пишем
fout.write(str(a + b) + "\n") # \n - перевод строки: следующее пойдёт ниже
fout.write(str(a * b))
fout.close()
| input.txt | output.txt |
|---|---|
8 5 |
13 и на следующей строке 40 |
📌
read()иwrite()работают со строками. Из файла число приходит как текст, и перед счётом его нужно превратить в число черезint(). А перед записью число превращаем в текст черезstr().
💡 Хочешь «без
write()»? Можно и так:print(a + b, file=fout). Толькоstr().
Почему close() обязателен
Питон не пишет каждую букву на диск сразу, а копит в «корзине» (буфере) и сбрасывает на диск пачкой. close() как раз выгружает корзину и освобождает файл. Проверим на примере.
f = open("t.txt", "w")
f.write("hi")
g = open("t.txt", "r") # открыли тот же файл для чтения, пока f не закрыт
print(repr(g.read())) # '' - пусто! "hi" ещё сидит в буфере
g.close()
f.close() # вот теперь "hi" на диске
g = open("t.txt", "r")
print(repr(g.read())) # 'hi'
g.close()
Программа выведет '', а потом 'hi'.
📕 В учебнике сказано: если не вызвать
close()в конце программы, записанные данные не сохранятся. На самом деле Питон при обычном завершении программы чаще всего сам сбрасывает буфер, и файл получается целым. Но полагаться на это нельзя: пока файл не закрыт, данные могут не дойти до диска, а читать такой файл в той же программе бесполезно (см. пример выше). Привычка «открыл, поработал, закрыл» убережёт тебя от потерянных данных.
👨👩👧 Родителю. Попросите ребёнка показать, где на компьютере лежит созданный программой файл, и открыть его в Блокноте. Это быстро проясняет, что «файл» это обычный документ, а не что-то магическое. Заодно спросите: «Что будет, если запустить программу в режиме w второй раз?»
Проверь себя
- Почему для 500 записей телефонной книги лучше файл, а не клавиатура? Данные записываются один раз и хранятся на диске. Программа читает их сама при каждом запуске, а вводить 500 строк вручную долго и легко ошибиться.
- Какие три действия нужны при работе с файлом? Открыть, прочитать или записать, закрыть.
- Чем отличаются режимы
'w'и'a'?'w'создаёт файл заново и стирает старое,'a'оставляет старое и дописывает новое в конец. - Зачем после
read()часто пишутsplit()иmap(int, ...)?read()отдаёт одну строку.split()режет её на части, аint()превращает каждую часть в число. - Что значит
"\n"вwrite()? Перевод строки: следующее записанное пойдёт с новой строки.
Решаем упражнения
Анализ, синтез, оценка
№ 1 · «Анализ», вопрос 1
Можно ли объединить в одну программу код создания входного файла (с числами) и код решения задачи про сумму и произведение? Объясни.
Как думать. Что случится, если читать файл, в который ещё пишет та же программа?
Решение. Можно, но между записью и чтением файл обязательно закрыть. Закроем записывающий file командой close(), и данные окажутся на диске. Только потом откроем файл на чтение.
file = open("input.txt", "w")
file.write("8 5")
file.close() # закрыли: теперь файл можно читать
fin = open("input.txt", "r")
a, b = map(int, fin.read().split())
fin.close()
fout = open("output.txt", "w")
fout.write(str(a + b) + "\n")
fout.write(str(a * b))
fout.close()
Запуск: output.txt получает 13 и 40.
Ответ: да, можно. Условие: сначала закрыть записанный файл, потом открыть его на чтение.
№ 2 · «Анализ», вопрос 2
Что получится, если убрать все команды close() из программы про сумму и произведение и запустить её?
Как думать. Вспомни «корзину» (буфер): данные уходят на диск при закрытии файла.
Решение. Если программа просто завершается, Питон обычно закрывает файлы сам, и output.txt получается правильным. Но это «авось», а не гарантия. А вот прочитать только что записанный файл в той же программе без close() не выйдет: он окажется пустым (мы это видели в примере с t.txt).
Ответ: при простом завершении программы файл, скорее всего, запишется сам. Но без close() данные могут не дойти до диска, а читать файл в той же программе нельзя. Поэтому close() ставим всегда.
№ 3 · «Анализ», вопрос 3
Можно ли использовать print() вместо write()? Обоснуй.
Как думать. Как сделать так, чтобы print писал не на экран, а в файл?
Решение. Можно. У print() есть параметр file: print(a + b, file=fout). Он сам превращает число в текст и в конце ставит перевод строки. Разница с write(): тот принимает только строки и ничего не добавляет сам.
fout = open("o.txt", "w")
print(8 + 5, file=fout) # в файл: 13 и перевод строки
print(8 * 5, file=fout) # в файл: 40 и перевод строки
fout.close()
Ответ: да, через print(..., file=fout). Он сам добавляет перевод строки, а write() нет.
№ 4 · «Синтез»
Придумай простое условие задачи, где используются запись в файл и чтение из файла. Режимы возьми из таблицы режимов.
Как думать. Подойдёт любая небольшая задача: прочитали числа, посчитали, записали, потом что-то дописали.
Пример ответа. Условие: в файле scores.txt через пробел лежат три оценки за контрольные. Запиши в report.txt лучшую из них, а на следующей строке допиши слово «Молодец!».
fin = open("scores.txt", "r", encoding="utf-8") # режим r: читаем
a, b, c = map(int, fin.read().split())
fin.close()
fout = open("report.txt", "w", encoding="utf-8") # режим w: пишем с нуля
fout.write(str(max(a, b, c)) + "\n")
fout.close()
fout = open("report.txt", "a", encoding="utf-8") # режим a: дописываем
fout.write("Молодец!")
fout.close()
Проверка: при 4 5 3 в report.txt будет 5 и на второй строке Молодец!.
Ответ: условие и программа выше (используем режимы 'r', 'w', 'a').
№ 5 · «Оценка»
Насколько важно уметь читать данные из файла при разработке программ? Оцени.
Как думать. Подумай, что было бы, если бы данные можно было вводить только руками.
Пример ответа. Очень важно. Из файла программа получает большие объёмы данных (список класса, результаты соревнований, настройки игры) и не просит человека набирать их заново. К тому же данные переживают программу: они остаются на диске. Один и тот же файл могут читать разные программы.
Ответ: чтение из файла нужно, чтобы работать с большими данными без ручного ввода и сохранять данные между запусками.
Вопросы для размышления
№ 6 · «Вопросы для размышления», вопрос 1
В чём разница между write() и print()?
Как думать. Сравни, куда пишут и что добавляют сами.
Решение. write() это метод файла: он пишет в файл и принимает только строки. Перевод строки сам не ставит, его добавляем "\n". print() по умолчанию выводит на экран, превращает всё в текст и сам ставит перевод строки. Если дать ему file=..., он тоже запишет в файл.
Ответ: write() пишет строку в файл без перевода строки, print() выводит на экран (или в файл через file=) и сам завершает строку переводом.
№ 7 · «Вопросы для размышления», вопрос 2
В чём разница между read() и input()? Обоснуй.
Как думать. Откуда каждая команда берёт данные и сколько?
Решение. input() ждёт, пока человек напечатает строку на клавиатуре, и возвращает одну строку. read() берёт данные из файла и возвращает весь оставшийся текст сразу, ничего у человека не спрашивая. У обеих результат строка, и для числа нужен int().
Ответ: input() читает одну строку с клавиатуры, read() читает из файла всё содержимое. Обе возвращают текст.
№ 8 · «Вопросы для размышления», вопрос 3
Зачем после открытия файла для записи обязательно использовать close()?
Как думать. Вспомни про «корзину» (буфер) и про занятые ресурсы.
Решение. Пока файл открыт, записанное может лежать в буфере, а не на диске. close() выгружает буфер на диск и освобождает ресурсы, которые файл занимал. Закрытый файл можно спокойно открыть снова или читать другой программой.
Ответ: чтобы данные точно попали на диск и файл освободился.
№ 9 · «Вопросы для размышления», вопрос 4
Какие различия есть между вводом данных из файла и вводом с клавиатуры?
Как думать. Сравни по удобству, объёму и повторяемости.
Пример ответа.
| С клавиатуры | Из файла |
|---|---|
| каждый раз набираешь заново | данные записаны один раз |
| удобно для пары значений | удобно для больших объёмов |
| легко ошибиться при вводе | данные можно проверить и исправить заранее |
input() |
open(), read(), close() |
Ответ: из файла данные берутся автоматически и повторно, подходят для больших объёмов. С клавиатуры вводят вручную, каждый раз заново.
Задание
№ 10 · «Задание», задача 1
Базовый уровень. В Input.txt лежат целые числа a, b и c через пробел. Найди их среднее арифметическое и запиши в Output.txt. Пример: 10 20 30 даёт 20.
Как думать. Читаем три числа, считаем (a + b + c) / 3, записываем строкой.
Решение.
fin = open("Input.txt", "r")
a, b, c = map(int, fin.read().split())
fin.close()
fout = open("Output.txt", "w")
fout.write(f"{(a + b + c) / 3:g}") # :g убирает лишний ".0": 20.0 станет 20
fout.close()
Проверка: для 10 20 30 в файле 20. Для 7 8 10 получится 8.33333 (формат :g оставляет 6 значащих цифр).
Ответ: 20. Формула: (a + b + c) / 3.
№ 11 · «Задание», задача 2
Базовый уровень. В a.txt записаны числа m и p через пробел. Найди произведение их последних цифр и запиши в b.txt. Пример: 15268 14785 даёт 40.
Как думать. Последняя цифра числа это остаток от деления на 10: m % 10.
Решение.
fin = open("a.txt", "r")
m, p = map(int, fin.read().split())
fin.close()
fout = open("b.txt", "w")
fout.write(str((m % 10) * (p % 10))) # скобки обязательны
fout.close()
Для 15268 14785: 15268 % 10 = 8, 14785 % 10 = 5, 8 * 5 = 40.
Ответ: в b.txt записано 40.
№ 12 · «Задание», задача 3
Средней сложности. В файл b.txt из предыдущего задания (там лежит 40) допиши на новой строке число 60, используя режим дозаписи.
Как думать. Режим дозаписи это 'a'. А новая строка потребует "\n". Файл заканчивается на 40 без перевода строки, поэтому \n ставим перед шестьюдесятью.
Решение.
fout = open("b.txt", "a") # a: дописываем в конец
fout.write("\n60") # перевод строки и число
fout.close()
| b.txt до | b.txt после |
|---|---|
40 |
40 и на следующей строке 60 |
Ответ: программа выше. Если открыть в режиме 'w', прежнее 40 потеряется.
№ 13 · «Задание», задача 4
Базовый уровень. Выведи в выходной файл a.txt прямоугольник из звёздочек: сверху и снизу сплошные линии из 15 звёзд, а по бокам две строки со звёздочкой в начале и в конце.
Как думать. Верхняя строка: "*" * 15. Средняя: звезда, 13 пробелов (всего 15 символов), звезда. Каждую строку заканчиваем "\n".
Решение.
fout = open("a.txt", "w")
line = "*" * 15 # сплошная линия
fout.write(line + "\n")
fout.write("*" + " " * 13 + "*\n") # середина: звезда, 13 пробелов, звезда
fout.write("*" + " " * 13 + "*\n")
fout.write(line)
fout.close()
Результат в файле:
***************
* *
* *
***************
Ответ: программа выше строит рамку 15 × 4.
№ 14 · «Задание», задача 5
Базовый уровень. Выведи в выходной файл input.txt фигуру: три горизонтальные линии из знаков «=» между вертикальными чёрточками, а между линиями две строки с двумя чёрточками посередине.
Как думать. Строим стенку "|" + "=" * 10 + "|" и промежуточную строку "|" + 4 пробела + "||" + 4 пробела + "|". Чередуем: стенка, промежуток, стенка, промежуток, стенка.
Решение.
fout = open("input.txt", "w")
wall = "|" + "=" * 10 + "|\n" # линия из знаков равно
gap = "|" + " " * 4 + "||" + " " * 4 + "|\n" # строка с двумя чёрточками
fout.write(wall + gap + wall + gap + wall)
fout.close()
Результат:
|==========|
| || |
|==========|
| || |
|==========|
Ответ: программа выше.
№ 15 · «Задание», задача 6
Базовый уровень. Выведи в output.txt «ёлочку» из цифр: строки 1, 222, 33333, 4444444, 555555555, по центру, между двумя линиями из «=».
Как думать. Строка с цифрой n содержит эту цифру 2n − 1 раз: 1, 3, 5, 7, 9. Чтобы выровнять по центру, используем .center(11). Это метод строки: он добавляет пробелы по краям до нужной ширины.
Решение.
fout = open("output.txt", "w")
border = "=" * 13 + "\n"
fout.write(border)
for n in range(1, 6):
row = str(n) * (2 * n - 1) # цифра n повторена 2n-1 раз
fout.write("|" + row.center(11) + "|\n")
fout.write(border)
fout.close()
Результат:
=============
| 1 |
| 222 |
| 33333 |
| 4444444 |
| 555555555 |
=============
Ответ: программа выше.
Домашнее задание
№ 16 · «Домашнее задание», задание 1
Что окажется в выходном файле после выполнения программы с рисунка? Проанализируй код и опиши результат письменно.
Код с рисунка:
file = open("data.txt", "w")
file.write('284 152 4589')
file.close()
fin = open("data.txt", "r")
p = fin.read().split()
a, b, c = map(int, p)
fout = open("Outdata.txt", "w")
k = a % 10 + b % 10 + c // 1000
fout.write(str(k) + '\n')
fin.close()
fout = open("Outdata.txt", "a")
fout.write('Sau bolynyz')
Как думать. Иди по строкам, как компьютер. Сначала файл data.txt, потом числа, потом считаем k.
Решение. Сумма k = 4 + 2 + 4 = 10. Режим 'a' оставляет 10, поэтому слова допишутся ниже. Я запускал этот код: в Outdata.txt оказались 10 и на второй строке Sau bolynyz. («Sau bolynyz» по-казахски «Будьте здоровы».)
| Outdata.txt |
|---|
10 |
Sau bolynyz |
📕 В коде с рисунка файл
foutв первый раз так и не закрыт: закрываетсяfin, а потомfoutпереоткрывается в режиме'a'. И в конце программы тоже нетclose(). Результат всё равно получается верным (Питон при выходе всё сбрасывает на диск), но правильно писатьfout.close()сразу после записи, перед повторным открытием.
Ответ: в выходном файле две строки: 10 и Sau bolynyz.
Шпаргалка
Термины: файл, текстовый файл, двоичный файл, режим открытия, чтение, запись, дозапись, закрытие файла.