3.5. Потоковый ввод/вывод. Работа с текстовыми файлами. JSON

В этом параграфе вы научитесь считывать данные не только с клавиатуры, но и из файлов, а также записывать результаты работы программ обратно в текстовые и JSON-файлы. Разберётесь, как устроен стандартный поток ввода и зачем нужен символ EOF, научитесь использовать функции open(), read(), write() и менеджер контекста with. Кроме того, освоите работу с форматом JSON — научитесь читать и сохранять структурированные данные с помощью модуля json. Эти знания помогут вам строить программы, которые взаимодействуют с внешними источниками данных и сохраняют результаты в удобном виде.

Ключевые вопросы параграфа

  • Что такое стандартный поток ввода и как с ним работать в Python?
  • Какие методы позволяют считывать и записывать данные из текстовых файлов?
  • Почему важно использовать менеджер контекста with при работе с файлами?
  • Как устроен формат JSON и чем он отличается от обычного текста?
  • Как использовать модуль json для чтения и записи структурированных данных?

Что такое стандартный поток ввода и как с ним работать в Python

Ранее вы уже использовали функцию input() для чтения данных с клавиатуры. Этот способ хорош, если вы точно знаете, сколько строк нужно ввести или как выглядит строка для завершения ввода.

В таких случаях помогает стандартный поток вводаstdin. Это поток, в который попадают все строки, вводимые пользователем. Программа может забирать их одну за другой, пока не достигнет конца ввода.

Подключение stdin

Чтобы использовать stdin, его нужно импортировать:

1from sys import stdin

Поскольку stdin — это итератор, по его строкам можно пройти в цикле:

1from sys import stdin
2
3lines = []
4for line in stdin:
5    lines.append(line)
6print(lines)

Запустим программу и введём следующие данные:

Первая
Вторая
Третья

Чтобы завершить ввод:

  • в Windows — нажмите Ctrl + Z, затем Enter;
  • в Linux и macOS — нажмите Ctrl + D.

Результат работы программы:

['Первая\n', 'Вторая\n', 'Третья\n']

Обратите внимание: каждая строка заканчивается символом перехода на новую строку (\n). Если он мешает, его можно удалить:

1from sys import stdin
2
3lines = []
4for line in stdin:
5    lines.append(line.rstrip("\n"))
6print(lines)
7
8# Теперь результат:
9# ['Первая', 'Вторая', 'Третья']

Чтение всех строк сразу

Если не нужен поэлементный перебор, можно считать все строки сразу с помощью readlines():

1from sys import stdin
2
3lines = stdin.readlines()
4print(lines)

Чтение всего ввода в одну строку

1from sys import stdin
2
3text = stdin.read()
4print([text])
5
6# Пример вывода:
7# ['Первая\nВторая\nТретья\n']

Здесь результат обёрнут в список, чтобы явно показать символы перехода на новую строку. Переменная text содержит весь ввод целиком — со всеми символами \n.

Какие методы позволяют считывать и записывать данные из текстовых файлов

Работа с файлами позволяет программам получать данные из внешних источников и сохранять результаты. Python предоставляет удобные инструменты для этого.

Что такое файл

Файл — это именованная область данных на диске. Данные в нём хранятся как последовательность байтов. Если файл текстовый, его можно интерпретировать как последовательность символов. Для корректной работы с символами нужно знать кодировку (чаще всего используется UTF-8).

Функция open()

1open(file, mode='r', encoding=None)

Аргумент mode определяет режим доступа к файлу:

  • 'r' — чтение (по умолчанию).
    Файл должен существовать, иначе будет ошибка.
  • 'w' — запись с очисткой содержимого.
    Если файл существует, его содержимое удаляется. Если не существует — создаётся.
  • 'a' — добавление в конец файла.
    Новый текст будет дописан после уже имеющегося. Если файл не существует — создаётся.
  • 'r+' — чтение и запись, без удаления содержимого.
    Файл должен существовать. Запись идёт поверх существующего текста, начиная с начала файла.
  • 'w+' — чтение и запись, с удалением содержимого.
    Файл очищается или создаётся заново. Подходит, если вы хотите сначала записать, а потом прочитать.
  • 'a+' — чтение и добавление.
    Файл открывается в режиме добавления, но можно также его читать. При записи — курсор всегда в конце.

Важно: в Windows по умолчанию используется кодировка cp1251, поэтому нужно всегда явно указывать кодировку UTF-8, если вы хотите, чтобы программа работала кросс-платформенно.

Прочитать документацию вы можете по ссылке.

Пример: открытие файла для чтения

Допустим, у нас есть файл input_1.txt, содержащий следующий текст:

1Привет!
2Это пример текстового файла.
3А это - последняя строка, которая заканчивается переходом на новую.

Прочитаем его содержимое:

1file_in = open("input_1.txt", encoding="UTF-8")
2for line in file_in:
3    print(line)
4file_in.close()

Построчное чтение файла

По открытому файлу можно пройти в цикле:

1file_in = open("input_1.txt", encoding="UTF-8")
2for line in file_in:
3    print(line)
4file_in.close()

Почему появляются пустые строки

Мы ожидали, что что строки выведутся одна под другой. Но фактически на экране будет:

1Привет!
2
3Это пример текстового файла.
4
5А это - последняя строка, которая заканчивается переходом на новую.

Причина: каждая строка из файла уже заканчивается символом переноса строки \n, а функция print() по умолчанию тоже добавляет \n. В результате между строками получается двойной отступ.

Как это исправить

Можно удалить символ переноса в конце строки с помощью .rstrip("\n"):

1print(line.rstrip("\n"))

Теперь вывод будет выглядеть так:

1Привет!
2Это пример текстового файла.
3А это - последняя строка, которая заканчивается переходом на новую.

Всё ровно — без лишних пустых строк.

Почему важно использовать менеджер контекста with при работе с файлами

Обычно после работы с файлом его нужно закрывать вручную:

1file_in = open("input_1.txt", encoding="UTF-8")
2# ... работа с файлом ...
3file_in.close()

Если этого не сделать, файл может остаться открытым:

  • он будет заблокирован для других программ;
  • данные могут не сохраниться сразу;
  • при ошибке в коде close() может не выполниться вовсе.

Безопасный способ — использовать with

После работы с файлом его необходимо закрыть с помощью метода close(). Это особенно важно, потому что:

  • незакрытый файл может остаться заблокированным для других программ;
  • данные могут не записаться полностью;
  • при ошибке в программе вызов close() может не выполниться, и файл останется открытым.

Чтобы избежать этих проблем, используйте менеджер контекста — конструкцию with. Она гарантирует, что файл будет закрыт автоматически, даже если в процессе работы произойдёт ошибка:

1with open("input_1.txt", encoding="UTF-8") as file_in:
2    for line in file_in:
3        print(line.rstrip("\n"))

Почему это ещё и быстрее

Следует помнить о том, что устройство хранения данных является самым медленным в компьютере, так что частые операции с файлами будут снижать скорость работы вашей программы.

Поэтому, если позволяют ресурсы компьютера (объём оперативной памяти), файл лучше прочитать сразу целиком, работать с ним в оперативной памяти и записывать в файл полностью полученный результат.

Чтение файла целиком — списком строк

Для получения полного списка строк текстового файла используется метод readlines():

1with open("input_1.txt", encoding="UTF-8") as file_in:
2    lines = file_in.readlines()
3print(lines)
4
5# Вывод программы:
6# ['Привет!\n', 'Это пример текстового файла.\n', 'А это -- последняя строка, которая должна закончиться переходом на новую.\n']

Чтение первых символов файла

Для чтения символов файла в строковую переменную используется метод read().
Прочитаем 10 символов файла:

1with open("input_1.txt", encoding="UTF-8") as file_in:
2    symbols = file_in.read(10)
3print([symbols])
4
5# Вывод программы:
6# ['Привет!\nЭт']

Если в методе read() не указать количество считываемых символов, то прочитается весь файл.

Запись в файл

Для записи в файл его необходимо сначала открыть в режиме записи (выше мы говорили о режимах доступа).
Для записи данных из строковой переменной используется метод write():

1with open("output_1.txt", "w", encoding="UTF-8") as file_out:
2    n = file_out.write("Это первая строка\nА вот и вторая\nИ третья -- последняя\n")
3
4print(n)

Что делает программа:

  1. Открывает (или создаёт) файл output_1.txt в режиме записи. Если файл уже существует, его содержимое будет удалено.
  2. Записывает в него одну строку с символами перевода строки \n внутри — это создаст три строки в файле.
  3. Метод write() возвращает количество записанных символов, включая все пробелы и \n.
  4. Эта длина (в нашем случае — 55) выводится в консоль.

Запись списка строк

Для записи строк из списка в файл используется метод writelines(). Этот метод записывает строки в файл по очереди, без разделителя. Пример его использования:

1lines = ["Это первая строка\n", "А вот и вторая\n", "И третья - последняя\n"]
2with open("output_2.txt", "w", encoding="UTF-8") as file_out:
3    file_out.writelines(lines)
4
5# Содержимое выходного файла:
6# Это первая строка
7# А вот и вторая
8# И третья - последняя

Использование print() для записи в файл

Функция print() может быть использована для вывода данных в файл.
Для этого нужно передать ей в аргумент file файловый объект:

1with open("output_3.txt", "w", encoding="UTF-8") as file_out:
2    print("Вывод в файл с помощью функции print()", file=file_out)
3
4# Содержимое выходного файла:
5# Вывод в файл с помощью функции print()

Как устроен формат JSON и чем он отличается от обычного текста

Существуют такие форматы текстовых файлов, которые могут хранить в себе информацию, структурированную по некоторым правилам.

Один из таких форматов — JSON (англ. JavaScript Object Notation). Как видно из названия, он был создан для языка JavaScript, но в настоящее время независим от него и может использоваться практически с любым языком программирования.

Для значений в JSON можно использовать:

  • строки — для обозначения строки используются строго двойные кавычки;
  • числа — целые и вещественные;
  • логические значения true и false — записываются, в отличие от Python, со строчной буквы.

Для хранения неупорядоченных наборов данных в JSON можно использовать записи — пары «ключ: значение» (аналог словаря в Python). Также можно хранить упорядоченные последовательности значений (аналог списка в Python).

Посмотрим, что представляет собой JSON-файл, на примере. Предположим, что нужно хранить структурированную информацию о студентах следующего вида:

  • фамилия (last_name),
  • имя (first_name),
  • отчество (patronymic),
  • дата рождения (date_of_birth),
  • номер группы (group_number),
  • список номеров телефонов (phones_numbers).

Тогда список с информацией о студентах можно представить в следующем виде:

1[
2  {
3    "last_name": "Иванов",
4    "first_name": "Иван",
5    "patronymic": "Иванович",
6    "date_of_birth": "01.01.2001",
7    "group_number": 1,
8    "phone_numbers": [
9      "+7 111 111 1111",
10      "+7 111 111 1112"
11    ]
12  },
13  {
14    "last_name": "Петров",
15    "first_name": "Пётр",
16    "patronymic": "Петрович",
17    "date_of_birth": "10.10.2001",
18    "group_number": 1,
19    "phone_numbers": [
20      "+7 111 111 1113",
21      "+7 111 111 1114"
22    ]
23  }
24]

Мы видим, что в JSON-файле находится список из двух записей, очень похожих на словарь Python.

Как использовать модуль json для чтения и записи структурированных данных

Для более удобной работы с JSON-файлами существует стандартный модуль json.
Он позволяет преобразовать данные из JSON-файла в вашей программе в стандартные типы данных Python, а также способен выполнить обратную операцию — для записи данных обратно в файл.

Чтение JSON-файла

Для чтения JSON-файлов используется метод load(), считывающий весь файл целиком и возвращающий объект стандартного типа данных Python:

1import json
2from pprint import pprint
3
4with open("data.json", encoding="UTF-8") as file_in:
5    records = json.load(file_in)
6pprint(records)
7
8#Вывод программы:
9# [{'date_of_birth': '01.01.2001',
10#   'first_name': 'Иван',
11#   'group_number': 1,
12#   'last_name': 'Иванов',
13#   'patronymic': 'Иванович',
14#   'phone_numbers': ['+7 111 111 1111', '+7 111 111 1112']},
15#  {'date_of_birth': '10.10.2001',
16#   'first_name': 'Пётр',
17#   'group_number': 1,
18#   'last_name': 'Петров',
19#   'patronymic': 'Петрович',
20#   'phone_numbers': ['+7 111 111 1113', '+7 111 111 1114']}]

Из примера видно, что JSON-файл был преобразован в список словарей, а каждый словарь — это запись с информацией о студенте. Для обработки таких объектов можно применять стандартные методы и операции Python.

Запись JSON-файла

После того как вы изменили структуру данных в программе (например, обновили одно из полей словаря), результат можно сохранить обратно в JSON-файл.

Для этого используется метод dump() из модуля json. Он преобразует объект Python в строку в формате JSON и записывает её в файл.

Рассмотрим важные аргументы функции json.dump():

  • ensure_ascii — если True (по умолчанию), все не-ASCII-символы сохраняются как \uXXXX. При False сохраняются как есть (нужно для букв на кириллице).
  • indent — задаёт отступы для форматирования:
    • None (по умолчанию) — весь JSON в одну строку;
    • число — количество пробелов на уровень вложенности.
  • sort_keys — если True, ключи в словаре будут отсортированы.

Пример

Изменим поле group_number у второго студента и запишем обновлённые данные обратно в файл:

1import json
2
3with open("data.json", encoding="UTF-8") as file_in:
4    records = json.load(file_in)
5records[1]["group_number"] = 2
6with open("data.json", "w", encoding="UTF-8") as file_out:
7    json.dump(records, file_out, ensure_ascii=False, indent=2)

Содержимое файла data.json после работы программы:

1[
2  {
3    "last_name": "Иванов",
4    "first_name": "Иван",
5    "patronymic": "Иванович",
6    "date_of_birth": "01.01.2001",
7    "group_number": 1,
8    "phone_numbers": [
9      "+7 111 111 1111",
10      "+7 111 111 1112"
11    ]
12  },
13  {
14    "last_name": "Петров",
15    "first_name": "Пётр",
16    "patronymic": "Петрович",
17    "date_of_birth": "10.10.2001",
18    "group_number": 2,
19    "phone_numbers": [
20      "+7 111 111 1113",
21      "+7 111 111 1114"
22    ]
23  }
24]

Преобразование ключей словаря

Модуль json автоматически преобразует ключи словаря в строки, если они были, например, целыми числами.
Рассмотрим пример:

1import json
2
3records = {
4    1: "First",
5    2: "Second",
6    3: "Third"
7}
8
9with open("output.json", "w", encoding="UTF-8") as file_out:
10    json.dump(records, file_out, ensure_ascii=False, indent=2)

Содержимое файла output.json после работы программы:

1{
2  "1": "First",
3  "2": "Second",
4  "3": "Third"
5}

Обратите внимание: ключи 1, 2, 3 были целыми числами в Python, но в JSON все ключи обязательно должны быть строками — они были автоматически преобразованы при записи.

✅ У вас получилось разобраться, как работать с файлами и JSON в Python?

👉 Оценить этот параграф

Что дальше

Теперь вы умеете считывать данные из стандартного потока ввода, читать и записывать текстовые файлы, работать с кодировкой, а также обрабатывать структурированные данные в формате JSON. Вы познакомились с менеджером контекста with, поняли, как важно закрывать файл, и научились использовать модуль json для преобразования данных.

Далее — финальный параграф третьей главы, в котором мы кратко подведём итоги.

А пока вы не ушли дальше — закрепите материал на практике:

  • Отметьте, что урок прочитан, при помощи кнопки ниже.
  • Пройдите мини-квиз, чтобы проверить, насколько хорошо вы усвоили тему.
  • Перейдите к задачам этого параграфа и потренируйтесь.
  • Перед этим загляните в короткий гайд о том, как работает система проверки.

Хотите обсудить, задать вопрос или не понимаете, почему код не работает? Мы всё предусмотрели — вступайте в сообщество Хендбука! Там студенты помогают друг другу разобраться.

Ключевые выводы параграфа

  • Стандартный поток ввода stdin позволяет считывать произвольное количество строк из консоли, что удобно при обработке входных данных без заранее известного объёма.
  • С помощью методов read(), readlines() и write() можно считывать и записывать данные из текстовых файлов, построчно или целиком.
  • Менеджер контекста with гарантирует, что файл будет закрыт автоматически, даже если в программе произойдёт ошибка.
  • Формат JSON используется для хранения структурированных данных и поддерживается большинством языков программирования, включая Python.
  • Модуль json позволяет легко преобразовывать JSON-файлы в объекты Python (load) и обратно (dump), а также управлять форматированием и кодировкой при записи.

Отмечайте параграфы как прочитанные чтобы видеть свой прогресс обучения

Вступайте в сообщество хендбука

Здесь можно найти единомышленников, экспертов и просто интересных собеседников. А ещё — получить помощь или поделиться знаниями.
Вступить
Сообщить об ошибке
Предыдущий параграф3.4. Встроенные возможности по работе с коллекциями

В этом параграфе вы познакомитесь с расширенными возможностями Python для работы с коллекциями. Вы научитесь использовать библиотеку itertools, чтобы эффективно обрабатывать и комбинировать данные, даже если они приходят из разных источников. Разберётесь, как создавать бесконечные итераторы, объединять и фильтровать коллекции, а также применять функции enumerate() и zip() в практических задачах. Эти инструменты помогут писать более компактный и читаемый код.

Следующий параграф3.6. Чему вы научились