Урок курса
Чтение текстового файла
Python для новичковДо этого урока мы работали с данными, которые уже были написаны прямо в коде — строки, списки, словари. Но в реальных задачах данные хранятся в файлах, и их нужно оттуда читать. Именно этим мы и займёмся: откроем текстовый файл и разберём, как Python с ним работает.
Открытие файла: функция open(), режим 'r' и кодировка utf-8
Точка входа в работу с файлом — встроенная функция open(). Она принимает несколько аргументов, и важно понять каждый из них.
Минимальный вызов выглядит так:
f = open('data.txt', 'r', encoding='utf-8')
Первый аргумент — путь к файлу. Можно указать просто имя файла, если он лежит в той же папке, что и скрипт, или полный путь, если файл в другом месте.
Второй аргумент — режим открытия. Строка 'r' означает «только чтение». В этом режиме файл должен уже существовать — если файла нет, Python выбросит FileNotFoundError. Режим 'r' стоит по умолчанию, то есть если не передать второй аргумент вовсе, Python откроет файл именно для чтения. Тем не менее писать его явно — хорошая привычка: код становится понятнее.
Третий аргумент — encoding='utf-8'. Файл на диске хранится как последовательность байт. Чтобы Python превратил эти байты в строки, ему нужно знать кодировку. UTF-8 — самая распространённая кодировка для текстовых файлов, и она корректно обрабатывает кириллицу. Если не указать encoding явно, Python возьмёт кодировку из системных настроек — на Windows это часто cp1251, на Linux и macOS — utf-8. Из-за этого один и тот же скрипт может читать файл правильно на одной машине и выдавать кашу символов на другой. Поэтому encoding='utf-8' стоит писать всегда.
Что возвращает open()? Она возвращает файловый объект, который также называют дескриптором файла. Это не сами данные из файла — это специальный объект, который представляет открытое соединение с файлом и предоставляет методы для чтения. В примере выше мы связали этот объект с именем f.
Дескриптор — это своего рода «канал» к файлу. Пока он открыт, через него можно читать данные. После того как работа с файлом завершена, дескриптор нужно закрыть — иначе операционная система продолжает держать файл занятым. Как именно обеспечить закрытие правильно — разберём в следующей секции.
Безопасное открытие файла конструкцией with open(...) as f
Допустим, мы открыли файл через open() и забыли его закрыть. Операционная система продолжает держать дескриптор занятым: другие программы могут не получить к файлу доступ, а данные, записанные в буфер, могут не сбросится на диск. Явный вызов f.close() решает проблему, но только если до него не случится ошибка — если где-то между open() и close() выбросится исключение, close() просто не выполнится.
Для этого в Python есть конструкция with:
with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
Как это работает: open() открывает файл и возвращает файловый объект, as f связывает его с именем f. Дальше выполняется тело блока with — всё, что написано с отступом. Когда блок заканчивается — неважно, штатно или из-за исключения — Python автоматически закрывает дескриптор. Явный f.close() писать не нужно.
Это называется контекстным менеджером: конструкция with берёт на себя управление ресурсом — открытие и закрытие — так, что программист не может случайно пропустить второй шаг.
Важный момент: за пределами блока with дескриптор f уже закрыт. Если попытаться обратиться к нему после выхода из блока, получим ошибку:
with open('data.txt', 'r', encoding='utf-8') as f:
pass
f.read() # ValueError: I/O operation on closed file
Поэтому всю работу с содержимым файла нужно делать внутри блока with — это не ограничение, а правильный способ структурировать код.
Чтение содержимого файла методами read() и readlines()
Когда файл открыт через with open(...) as f, дескриптор f предоставляет два основных метода для извлечения текста: read() и readlines(). Они решают одну задачу — достать содержимое файла — но возвращают разные типы данных.
Метод read()
read() читает файл целиком и возвращает всё содержимое как одну строку str.
with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)
print(type(content)) # <class 'str'>
Если файл data.txt содержит три строки:
яблоко банан вишня
то переменная content после read() будет выглядеть так:
'яблоко\nбанан\nвишня\n'
Это одна строка, в которой символы \n отмечают границы строк файла. Такой результат удобен, когда нужно работать с текстом целиком — например, искать подстроку или передать весь текст куда-то дальше одним куском.
Одна особенность: после вызова read() позиция чтения смещается в конец файла. Если вызвать read() ещё раз на том же дескрипторе, вернётся пустая строка — читать уже нечего. Это не ошибка, просто нужно учитывать.
Метод readlines()
readlines() тоже читает файл целиком, но не склеивает его в одну строку, а возвращает список строк — list[str]. Каждый элемент списка соответствует одной строке файла.
with open('data.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
print(lines)
print(type(lines)) # <class 'list'>
Результат для того же файла:
['яблоко\n', 'банан\n', 'вишня\n']
Каждый элемент включает завершающий \n — кроме последней строки, если файл не заканчивается переносом. Список удобен тогда, когда нужно работать со строками по отдельности: обращаться к конкретной по индексу, считать их количество через len(lines), фильтровать.
Когда что использовать
Выбор между read() и readlines() зависит от того, как именно вы собираетесь обрабатывать данные.
read()— если нужна одна строка: поиск подстроки, разбивка вручную, передача текста целиком.readlines()— если сразу нужен список: индексный доступ, проверка числа строк, работа с отдельными элементами.
Оба метода загружают весь файл в память сразу. Для небольших файлов это нормально. Если файл большой — на это стоит обратить внимание, но для большинства задач начального уровня это не проблема.
Построчный перебор файла в цикле for и обработка символа \n
Файловый объект, который возвращает open(), является итерируемым. Это значит, что его можно поставить прямо в for — и Python будет отдавать по одной строке за раз, не загружая весь файл в память сразу.
with open('data.txt', 'r', encoding='utf-8') as f:
for line in f:
print(line)
Если data.txt содержит:
яблоко банан вишня
вывод будет выглядеть неожиданно:
яблоко банан вишня
Между строками — пустые строки. Откуда они берутся? print() сам добавляет перенос строки в конце. Но каждый элемент line уже содержит \n из файла — тот самый символ, который разделяет строки в тексте. Итого: \n из файла плюс \n от print() — и получается пустая строка между каждым выводом.
Фиксируется одним вызовом .strip():
with open('data.txt', 'r', encoding='utf-8') as f:
for line in f:
print(line.strip())
Метод strip() убирает пробельные символы с обоих концов строки — в том числе \n и пробелы. Результат становится предсказуемым:
яблоко банан вишня
Если нужно убрать только правый \n, не трогая возможные пробелы слева, используют rstrip(). На практике чаще пишут просто strip() — это безопаснее для большинства случаев.
Когда цикл предпочтительнее read() и readlines()
Цикл for line in f читает файл построчно: Python запрашивает следующую строку только когда предыдущая уже обработана. Это важно, если файл большой — скажем, логи на несколько гигабайт. read() и readlines() загрузили бы такой файл целиком в оперативную память, а цикл работает с ним постепенно.
Для файлов из десятков строк разница несущественна. Но привычка использовать цикл там, где нужна построчная обработка, — правильная.
Часто при переборе нужно не просто вывести строки, а что-то с ними сделать — проверить условие, накопить результат. Вот типичный сценарий: отфильтровать непустые строки:
with open('data.txt', 'r', encoding='utf-8') as f:
result = []
for line in f:
cleaned = line.strip()
if cleaned: # пропускаем пустые строки
result.append(cleaned)
print(result) # ['яблоко', 'банан', 'вишня']
Здесь if cleaned проверяет, что после strip() строка не стала пустой. Пустая строка в Python — это False, поэтому проверка работает напрямую, без сравнения с ''.
Эта связка — for line in f + strip() + условие внутри цикла — встречается в реальном коде постоянно, как только нужно разобрать текстовый файл построчно.
Попробуйте решить
Что означают аргументы 'r' и encoding='utf-8' в вызове open()?
Продолжить с проверкой и прогрессом
Откройте интерактивный раннер с заданиями урока.
