Перейти к основному содержимому

Работа с файлами

Данные датчиков проходят через Python-программу и сохраняются в файлы

Пока программа работает только с переменными, её данные живут в оперативной памяти и исчезают после завершения процесса. Для робототехнического эксперимента этого часто недостаточно: нужно сохранить расстояния с датчика, вспомнить коэффициенты калибровки при следующем запуске или передать журнал измерений в программу построения графиков. Файл позволяет оставить такие данные на накопителе и снова открыть их позже.

Коротко

Python открывает файл функцией open() или методом Path.open(). Режим определяет, будет программа читать, перезаписывать или дополнять данные. Конструкция with закрывает файл после выхода из блока даже при ошибке, а encoding="utf-8" делает работу с русским текстом предсказуемой.

Зачем это нужно

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

  • когда робот приблизился к стене;
  • как менялись показания датчика;
  • сработал ли порог остановки вовремя;
  • какие настройки использовались во время испытания.

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

Главная идея

Файл похож на подписанный контейнер, который остаётся после завершения программы. Работа с ним состоит из четырёх действий:

путь -> открыть в нужном режиме -> прочитать или записать -> закрыть

В Python эти действия обычно объединяет блок with:

from pathlib import Path

message_path = Path("status.txt")

with message_path.open("w", encoding="utf-8") as status_file:
status_file.write("Стенд готов к запуску\n")

После окончания блока объект status_file закрыт. Это освобождает связанный с ним системный ресурс и завершает обычную запись буферизованных данных.

Что такое путь

Путь указывает, где находится файл. Относительный путь вроде data/run.csv отсчитывается от текущей рабочей папки процесса. Абсолютный путь начинается от корня файловой системы. Для учебных проектов удобнее собирать пути через pathlib.Path, не склеивая разделители вручную.

Выбираем режим открытия

Второй аргумент open() задаёт режим. Ошибка в одной букве может стереть старый журнал, поэтому режим выбирают до записи.

РежимДействиеЕсли файла нетЧто будет со старым содержимым
rчтениеFileNotFoundErrorостанется без изменений
wзаписьфайл будет создансодержимое будет очищено
aдобавление в конецфайл будет созданстарые данные сохранятся
xсоздание нового файлафайл будет создансуществующий файл вызовет FileExistsError
b в составе режимадвоичные данные, например rbзависит от основного режимапрограмма работает с bytes, а не со строками

Если режим не указан, используется r. В текстовом режиме стоит явно задавать кодировку:

from pathlib import Path

notes_path = Path("experiment.txt")

with notes_path.open("a", encoding="utf-8") as notes_file:
notes_file.write("Порог остановки: 18 см\n")

Режим a подходит для последовательного журнала: новый запуск добавит строку, а не удалит прошлые записи. Для изображения, прошивки или другого двоичного формата используют режим с b; параметр encoding тогда не задают.

Path помогает не путать папки и файлы

Модуль pathlib представляет путь объектом. Такой объект умеет присоединять части пути, создавать папки и открывать файл:

from pathlib import Path

data_dir = Path("data")
data_dir.mkdir(exist_ok=True)

report_path = data_dir / "report.txt"
report_path.write_text("Испытание завершено\n", encoding="utf-8")

report = report_path.read_text(encoding="utf-8")
print(report.strip())

Методы write_text() и read_text() удобны для небольших файлов. Когда журнал может стать большим, лучше читать его построчно: тогда программа не загружает всё содержимое в память одновременно.

from pathlib import Path

log_path = Path("data") / "distance.log"

if log_path.exists():
with log_path.open("r", encoding="utf-8") as log_file:
for line_number, line in enumerate(log_file, start=1):
clean_line = line.strip()
if clean_line:
print(line_number, clean_line)

Проверка exists() полезна для ожидаемо необязательного файла. Если отсутствие файла означает неисправность проекта, ошибку лучше не скрывать, а обработать и сообщить пользователю, какой путь не найден.

Текст, CSV и JSON решают разные задачи

Формат выбирают по структуре данных, а не по расширению, которое выглядит привычнее.

ФорматЧто удобно хранитьПример в робототехникеОсобенность
обычный текстсообщения и простой журналсобытия запуска и остановкилегко читать человеку, структуру задаёт программа
CSVстроки с одинаковыми столбцамивремя, расстояние, скоростьудобно открывать как таблицу
JSONименованные настройки и простые наборы значенийнастройки датчиков и режимовсохраняет структуру базовых данных Python

Записываем измерения в CSV

Стандартный модуль csv учитывает правила табличного формата. При открытии файла документация рекомендует передавать newline="":

import csv
from pathlib import Path

samples = [
(0.0, 42.5),
(0.1, 37.8),
(0.2, 29.4),
(0.3, 17.9),
]

log_path = Path("data") / "distance.csv"
log_path.parent.mkdir(exist_ok=True)

with log_path.open("w", encoding="utf-8", newline="") as log_file:
writer = csv.writer(log_file)
writer.writerow(["time_s", "distance_cm"])
writer.writerows(samples)

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

Сохраняем настройки в JSON

JSON подходит для набора именованных параметров. В Python такой набор удобно записать в фигурных скобках: слева имя параметра, справа его значение. Подробные коллекции не выделяются в отдельный урок, поэтому здесь достаточно читать запись буквально как «ключ -> значение». Стандартный модуль json выполняет сериализацию — превращает структуру Python в текстовое представление — и обратную десериализацию.

import json
from pathlib import Path

settings = {
"stop_distance_cm": 18,
"max_speed": 0.45,
"sound_enabled": True,
}

settings_path = Path("data") / "robot-settings.json"
settings_path.parent.mkdir(exist_ok=True)

with settings_path.open("w", encoding="utf-8") as settings_file:
json.dump(settings, settings_file, ensure_ascii=False, indent=2)

with settings_path.open("r", encoding="utf-8") as settings_file:
loaded_settings = json.load(settings_file)

print(loaded_settings["stop_distance_cm"])

Параметр indent=2 делает файл удобнее для проверки человеком. ensure_ascii=False оставляет русские символы читаемыми; сам JSON-файл при этом открывается в UTF-8.

Журнал измерений и настройки проходят разные пути хранения

Пример: журнал безопасной остановки

Соединим показания датчика и файл. Здесь список имитирует уже полученные измерения; в настоящем роботе числа приходят из драйвера датчика.

import csv
from pathlib import Path

STOP_DISTANCE_CM = 18.0
measurements = [31.2, 24.7, 17.6, 16.9]

log_path = Path("data") / "safety-run.csv"
log_path.parent.mkdir(exist_ok=True)

with log_path.open("w", encoding="utf-8", newline="") as log_file:
writer = csv.DictWriter(
log_file,
fieldnames=["step", "distance_cm", "motor_command"],
)
writer.writeheader()

for step, distance_cm in enumerate(measurements):
command = "stop" if distance_cm < STOP_DISTANCE_CM else "forward"
writer.writerow(
{
"step": step,
"distance_cm": distance_cm,
"motor_command": command,
}
)

Теперь решение программы связано с исходным измерением. Инженер может проверить строку, на которой команда сменилась с forward на stop, и сравнить её с выбранным порогом.

Что делать с ошибками чтения

Файл может отсутствовать, быть повреждённым или находиться в недоступной папке. Обрабатывайте только те ошибки, для которых программа знает понятное действие:

import json
from pathlib import Path

settings_path = Path("data") / "robot-settings.json"

try:
with settings_path.open("r", encoding="utf-8") as settings_file:
settings = json.load(settings_file)
except FileNotFoundError:
print("Файл настроек не найден. Используются безопасные значения.")
settings = {"stop_distance_cm": 15}
except json.JSONDecodeError as error:
print(f"JSON повреждён: строка {error.lineno}, столбец {error.colno}")
settings = {"stop_distance_cm": 15}

print(settings["stop_distance_cm"])

Значение по умолчанию должно быть действительно безопасным для конкретного робота. Универсального расстояния остановки нет: оно зависит от скорости, торможения, датчика и условий стенда.

Что запомнить о файлах

  1. Сначала определите путь, формат и режим открытия.
  2. Для текста явно задавайте encoding="utf-8".
  3. Используйте with, чтобы файл корректно закрывался.
  4. Помните, что w очищает существующее содержимое, а a дописывает в конец.
  5. Читайте большой журнал построчно.
  6. Храните табличные измерения в CSV, а структурированные настройки — в JSON.
  7. Не скрывайте ошибку, если программа не знает, как безопасно продолжить работу.

Практика

Задание 1. Журнал освещённости

Создайте список из десяти пар «время — освещённость». Запишите его в CSV с заголовками столбцов. Затем прочитайте файл и выведите только строки, где освещённость ниже выбранного порога.

Задание 2. Настройки движения

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

Задание 3. Защита старого эксперимента

Измените программу журнала так, чтобы она не могла случайно перезаписать уже существующий файл. Продумайте сообщение, которое увидит оператор при конфликте имён.

Задание 4. Анализ маршрута

В текстовом файле каждая строка содержит одну команду: forward, left, right или stop. Подсчитайте количество команд каждого вида, не загружая файл целиком в список.

Проверьте себя

  • Чем режим w отличается от a и x?
  • Зачем указывать encoding="utf-8"?
  • Почему файл удобно открывать внутри with?
  • Когда CSV подходит лучше JSON?
  • Чем чтение read_text() отличается от перебора файла в цикле?
  • Какая ошибка возникает при чтении отсутствующего файла?
  • Почему значение по умолчанию для робота должно быть безопасным?
Ориентиры для самопроверки

Сначала ответьте без подсказки. Ответ можно считать полным, если вы:

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

Если один из пунктов объяснить не получается, найдите соответствующую главу статьи, перечитайте её и повторите ответ.

Словарь статьи

  • Файл — именованная последовательность данных на накопителе.
  • Путь — указание расположения файла или папки.
  • Относительный путь — путь, который отсчитывается от текущей рабочей папки процесса.
  • Режим открытия — правило чтения, записи, добавления или создания файла.
  • Кодировка — способ сопоставить символы и байты; для учебных текстовых файлов используем UTF-8.
  • Контекстный менеджер — объект, который управляет входом в блок with и выходом из него.
  • Сериализация — преобразование структуры данных в форму для хранения или передачи.
  • CSV — текстовый формат строк с одинаковым набором столбцов.
  • JSON — текстовый формат для структурированных данных: объектов, массивов, строк, чисел и логических значений.

Связанные темы

Источники