4.2. Функции для работы с файлами
Информатика, 7 класс. Учебник: Кадыркулов Р. А., Рыскулбекова А. Д., Нурмуханбетова Г. К. (Алматыкітап баспасы, 2023). Раздел 4: Программирование алгоритмов на языке Python. Страницы 111–114.
Чему научишься
- Читать файл по строкам функцией
readline(), целиком или первые n символов. - Ставить «курсор» в нужное место файла функцией
seek(n). - Читать ровно n символов методом
read(n). - Заменять часть данных в файле, записав новые символы с нужной позиции.
- Решать задачи, где числа в файле надо «вырезать» по позициям.
Разминка
- Какие функции и методы для работы с файлами ты уже знаешь?
open(),read(),write(),close(). А ещёsplit(), чтобы разрезать прочитанную строку. - На что обращать особое внимание при записи в файл и чтении из файла? Выбрать правильный режим (
'r','w','a'), помнить, чтоread()иwrite()работают со строками, и обязательно закрывать файл.
Разбираемся
Двоичные файлы: просто запомни
В прошлый раз мы работали с текстовыми файлами. Для двоичных (картинки, музыка) к режиму добавляют букву 'b': например, чтобы читать двоичный файл, пишут 'rb' вместо 'r'. Пока мы работаем только с текстом, а двоичными файлами займёшься в старших классах.
readline(): читаем по строке
У файла есть строки: каждая заканчивается переводом строки \n. Функция readline() читает одну строку.
- Без параметра
readline()читает строку целиком, до\nвключительно. - С параметром
readline(n)читает только первые n символов строки.
Возьмём файл input.txt из двух строк (числа и буквы здесь мои):
qwertyuiopasdfgh
9876543210
Хотим первые 6 букв первой строки и первые 3 цифры второй.
fin = open("input.txt", "r")
p = fin.readline(6) # первые 6 символов первой строки: 'qwerty'
rest = fin.readline() # остаток первой строки: 'uiopasdfgh\n'
h = fin.readline(3) # первые 3 символа второй строки: '987'
fin.close()
Зачем нужен «пустой» readline() посередине? Чтобы перескочить остаток первой строки. Иначе следующий readline(3) продолжил бы с буквы u, а не с цифр.
💡 Курсор в файле как палец на строчке в книге: прочитал кусок, палец сдвинулся вперёд.
seek(): поставь курсор куда нужно
Обычно read() идёт по файлу от начала до конца. Функция seek(n) позволяет переставить курсор в позицию n и читать (или писать) оттуда. Позиции считаются с нуля: первый символ это позиция 0.
Возьмём файл 0123456789.
f = open("input.txt", "r")
f.seek(4)
print(f.read(3)) # 456 (читаем 3 символа с позиции 4)
print(f.read(3)) # 789 (курсор продолжил с места остановки)
f.seek(0) # вернули курсор в начало
print(f.read()) # 0123456789 (без параметра: всё до конца)
print(f.read()) # пусто: курсор уже в конце
f.close()
⚠️ Частая ошибка. Считать позиции с единицы. Символ
0123456789с номером 4 это цифра4, потому что нумерация идёт с нуля.
⚠️ Частая ошибка. Забыть, что курсор двигается сам после каждого чтения. Второй
read(3)продолжит с того места, где закончился первый. Чтобы начать заново, нуженseek(0).
Заменяем кусок файла
Если поставить курсор в середину и записать новые символы, они затрут старые на тех же местах. Остальное не меняется. Пример: в файле алфавит abcdefghijklmnopqrstuvwxyz, хотим заменить пять букв, начиная с позиции 5, значком #.
f = open("input.txt", "w")
f.write("abcdefghijklmnopqrstuvwxyz") # записали алфавит
f.seek(5) # курсор на позицию 5: буква f
f.write("#####") # затёрли пять букв: f g h i j
f.close()
Файл станет таким: abcde#####klmnopqrstuvwxyz.
⚠️ Частая ошибка. В этих примерах файл содержит только латинские буквы и цифры, где один символ занимает одну позицию. С русским текстом в кодировке UTF-8 позиции
seek()считаются иначе, поэтому для упражнений урока берём латиницу и цифры.
📌 Схема для чисел, записанных в файле «как попало»:
seek(позиция)(встать) →read(длина)(взять столько символов) →int(...)(превратить в число).
👨👩👧 Родителю. Закладка в книге прекрасно объясняет курсор. Спросите ребёнка: «Куда встанет закладка после того, как ты прочитал абзац? А если я попрошу открыть страницу 50?» Это
read()иseek()на пальцах.
Проверь себя
- Чем
readline()без параметра отличается отreadline(5)? Без параметра читается вся строка до перевода строки. С параметром 5 читаются первые пять символов. - Что делает
seek(n)? Переставляет курсор в файле на позицию n: дальше чтение или запись идёт оттуда. - Как прочитать ровно 4 символа начиная с позиции 7?
f.seek(7), затемf.read(4). - Что будет, если после
seek()не читать, а записать? Новые символы затрут старые на этих позициях. - Зачем в примере с буквами и цифрами понадобился «пустой»
readline()между двумя чтениями? Чтобы пропустить остаток первой строки и перейти ко второй.
Решаем упражнения
Практическая работа
№ 1 · «Практическая работа», задание 1
Файл Input.txt содержит трёх-, четырёх- и пятизначное числа через пробел: 256 4584 69581. Посчитай их сумму и запиши в Output.txt. Должно получиться 74421.
Как думать. Числа записаны подряд, длины известны: 3, 4, 5 цифр. Значит, можно вырезать каждое по позиции. Позиции считаем с нуля: 256 с позиции 0, пробел на 3, 4584 с позиции 4, пробел на 8, 69581 с позиции 9.
Как сделать. Шаги: открыть оба файла; прочитать три числа по позициям; превратить в числа через int(); записать сумму как строку; закрыть оба файла. Входной файл создай в Блокноте или программой: в него просто надо записать 256 4584 69581.
fin = open("Input.txt", "r")
fout = open("Output.txt", "w")
a = int(fin.read(3)) # трёхзначное с позиции 0
fin.seek(4)
b = int(fin.read(4)) # четырёхзначное с позиции 4
fin.seek(9)
c = int(fin.read(5)) # пятизначное с позиции 9
fout.write(str(a + b + c))
fin.close()
fout.close()
Запуск: в Output.txt лежит 74421.
💡 Есть и проще:
numbers = map(int, fin.read().split()), а потомsum(numbers). Он режет по пробелам и не зависит от позиций. Но урок проseek(), поэтому учимся вырезать. Я проверил оба варианта: оба дают74421.
Ответ: 74421.
Задание
№ 2 · «Задание», задача 1
Базовый уровень. В файле строка ajsjsidmmkkkddmdmd. Что получится после серий команд: а) seek(3), read(5); б) seek(5), read(); в) seek(0), read(1)?
Как думать. Выпиши буквы с номерами: a0 j1 s2 j3 s4 i5 d6 m7 m8 k9 k10 k11 d12 d13 m14 d15 m16 d17.
Решение.
а) seek(3): курсор на позиции 3 (буква j). read(5) берёт пять символов: j, s, i, d, m, то есть jsidm.
б) seek(5): курсор на i. read() читает до конца: idmmkkkddmdmd.
в) seek(0): в начало. read(1) читает один символ: a.
Я проверил запуском: 'jsidm', 'idmmkkkddmdmd', 'a'.
Ответ: а) jsidm; б) idmmkkkddmdmd; в) a.
№ 3 · «Задание», задача 2
Средней сложности. В файле строка 65 859 365. Какие команды нужны, чтобы выделить из неё числа 59 и 65?
Как думать. Пронумеруй символы: 6(0) 5(1) пробел(2) 8(3) 5(4) 9(5) пробел(6) 3(7) 6(8) 5(9). Нужное число занимает две позиции подряд.
Решение.
- Число 59 стоит в середине второго числа, цифры на позициях 4 и 5. Команды:
seek(4)иread(2), получим59. - Число 65 стоит в самом начале:
seek(0)иread(2), получим65. Но65есть ещё и в конце строки (позиции 8 и 9):seek(8)иread(2)дадут тоже65.
f = open("s.txt", "r")
f.seek(4)
print(f.read(2)) # 59
f.seek(0)
print(f.read(2)) # 65
f.seek(8)
print(f.read(2)) # 65 (из конца строки)
f.close()
Ответ: для 59: seek(4), read(2); для 65: seek(0), read(2) (или seek(8), read(2)).
№ 4 · «Задание», задача 3
Средней сложности. В Input.txt целые числа a, b, c, d и p через пробел. Найди сумму a и b, разность c и d. Каждое из этих двух чисел раздели на p, а остатки перемножь. Результат запиши в Output.txt. Пример: 25 18 45 12 9 даёт 42.
Как думать. Остаток от деления это %. Значит, нужно ((a + b) % p) * ((c - d) % p). Скобки важны: без них порядок действий другой.
Решение.
fin = open("Input.txt", "r")
a, b, c, d, p = map(int, fin.read().split()) # пять чисел сразу
fin.close()
r1 = (a + b) % p # остаток от деления суммы на p
r2 = (c - d) % p # остаток от деления разности на p
fout = open("Output.txt", "w")
fout.write(str(r1 * r2))
fout.close()
Запуск на 25 18 45 12 9: в файле 42.
Ответ: ((a + b) % p) * ((c - d) % p); для примера 42.
№ 5 · «Задание», задача 4
⭐ повышенной сложности. В sum.txt четыре действительных двузначных числа с сотыми долями: 12.74 15.68 98.18 32.84. Округли их до целых, найди сумму, запиши в Result.txt.
Как думать. Числа превращаем в вещественные через float(). Округляет функция round(). Но порядок действий важен: можно сначала округлить каждое, а потом сложить, а можно сначала сложить, а потом округлить.
Решение. Как сказано в условии, сначала округляем каждое число: 13, 16, 98, 33. Сумма 13 + 16 + 98 + 33 = 160.
fin = open("sum.txt", "r")
nums = map(float, fin.read().split()) # четыре вещественных числа
fin.close()
total = 0
for x in nums:
total += round(x) # округлили каждое, потом прибавили
fout = open("Result.txt", "w")
fout.write(str(total))
fout.close()
Запуск: в Result.txt будет 160.
📕 В учебнике для этих данных ответ
159. Но если следовать условию («округлите числа и вычислите их сумму»), получается160. А159выходит, если сначала сложить числа (159,44), а потом округлить итог:round(sum(nums)). Решай по тексту условия: сначала округлить каждое. Если учитель ждёт159, используй второй вариант.
⚠️ Частая ошибка.
round()в Питоне округляет «до чётного» ровно посередине:round(2.5)даёт 2, аround(3.5)даёт 4. В этих данных таких чисел нет, но запомни.
Ответ: 160 (округляем каждое число, как сказано в условии). Если сначала сложить: 159.
№ 6 · «Задание», задача 5
⭐ повышенной сложности. Во входном файле a.txt записано математическое выражение, например 1456+78544. Составь программу, которая вычислит его значение и выведет на экран. Для примера должно быть 80000.
Как думать. Из файла приходит строка '1456+78544'. Питон умеет вычислять выражение, записанное строкой, функцией eval(). Другой путь: разрезать строку по знаку + и сложить части.
Решение.
Вариант 1, самый короткий:
fin = open("a.txt", "r")
expr = fin.read().strip() # strip() убирает лишние пробелы и перевод строки
fin.close()
print(eval(expr)) # eval вычисляет выражение из строки
Вариант 2, если в файле только сложение:
fin = open("a.txt", "r")
expr = fin.read().strip()
fin.close()
x, y = expr.split("+") # '1456' и '78544'
print(int(x) + int(y))
Оба варианта я запускал: выводят 80000. С eval ещё посчитается и 300*12-1600: получится 2000.
⚠️ Частая ошибка.
eval()выполняет любой текст как код Питона. Использовать его можно только для своих файлов, а чужим данным доверять нельзя.
Ответ: 80000. Программа: print(eval(expr)), где expr прочитано из файла.
Домашнее задание
№ 7 · «Домашнее задание»
«Письмо другу». Напиши другу письмо о том, как прочитать данные из файла. Расскажи про особенности чтения из файла и его значение в программировании, с какими трудностями ты столкнулся и какие новые возможности в программировании открывает эта тема.
Как думать. Письмо живое, как сообщение другу. План: что узнал, как это делается, где было сложно, что теперь умеешь.
Пример ответа.
Привет, Арман!
Сегодня на информатике я узнал, как программа читает данные из файла. Теперь мне не нужно каждый раз вводить числа с клавиатуры. Я записываю их в текстовый файл, а программа сама их открывает: open("input.txt", "r"), потом read(), а потом обязательно close().
Ещё есть классная штука seek(). Это курсор: ставишь его на нужную позицию и читаешь только то, что надо, например read(4). Сначала я путался, что позиции считаются с нуля. Из-за этого сдвигался на один символ. А ещё из файла всё приходит строкой, и для чисел нужен int().
Теперь можно делать справочники, записи результатов и даже читать выражение из файла и считать его. Попробуй сам! Начни с файла из двух чисел.
Пока! Айдар
Ответ: свободное письмо; в нём стоит упомянуть open, read/readline, seek, close, нумерацию с нуля и перевод строки в число.
Шпаргалка
Термины: параметр функции, readline(), seek(), курсор, позиция, режим 'b'.