Python: устойчивые способы записи данных в файлы и их чтение

Раздел: Ввод-вывод и файловая система -> Файловый ввод-вывод

Основные подходы к хранению данных в файлах

JSON: универсальное текстовое хранение

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

import json

data = {
    'user': 'alice',
    'scores': [95, 88, 72],
    'active': True,
    'profile': {'age': 30, 'city': 'Moscow'}
}

with open('data.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

with open('data.json', 'r', encoding='utf-8') as f:
    loaded = json.load(f)

print(loaded)

ввод программ на python (ввод данных в программе python)

{'user': 'alice', 'scores': [95, 88, 72], 'active': True, 'profile': {'age': 30, 'city': 'Moscow'}}

Python file io (ввод-вывод файлов в python)

Пошаговое пояснение:

  • json.dump(data, f, ...) – сериализует объект Python в файл.
  • Параметр ensure_ascii=False разрешает кириллицу.
  • indent=2 делает вывод форматированным.
  • При загрузке json.load(f) восстанавливает объект.

Типичные проблемы и ошибки:

  • Файла не существует – возникает FileNotFoundError. Решение: проверять существование через os.path.exists или обрабатывать исключение.
  • Содержимое файла не является валидным JSON – json.JSONDecodeError. Причина: ручное редактирование, повреждение данных. Рекомендуется использовать try/except.
  • JSON не умеет сериализовать произвольные объекты (например, datetime). Можно написать свой кодировщик или преобразовать в строку.
  • Проблема с кодировкой – если файл открыт без encoding='utf-8', возможны ошибки на Windows.

Цели и случаи использования:

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

Как сохранить произвольные Python объекты без преобразования?

Модуль pickle позволяет сериализовать практически любой объект Python (классы, функции, исключения). Формат бинарный, нечитаемый человеком.

import pickle

class Account:
    def __init__(self, owner, balance):
        self.owner = owner
        self.balance = balance

acc = Account('Alice', 1000.50)

with open('account.pkl', 'wb') as f:
    pickle.dump(acc, f)

with open('account.pkl', 'rb') as f:
    acc_loaded = pickle.load(f)

print(acc_loaded.owner, acc_loaded.balance)

Python temp files (временные файлы в python)

Alice 1000.5

Python index files (индексация файлов в python)

Ошибки и риски:

  • pickle.UnpicklingError – если файл повреждён или создан другой версией Python.
  • Критическая уязвимость: загрузка pickle‑файла из ненадёжного источника может выполнить произвольный код. Использовать только для доверенных данных.
  • Несовместимость версий Python: pickle может не загружаться после обновления интерпретатора.

Использование: временное сохранение состояния, кеширование объектов, передача через сокет внутри защищённой сети.


Как хранить табличные данные в текстовом виде?

Формат CSV (Comma‑Separated Values) удобен для строк с одинаковыми полями. Модуль csv встроен в Python.

import csv

rows = [
    {'name': 'Alice', 'age': 30, 'score': 95},
    {'name': 'Bob', 'age': 25, 'score': 88}
]

with open('users.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.DictWriter(f, fieldnames=['name', 'age', 'score'])
    writer.writeheader()
    writer.writerows(rows)

with open('users.csv', 'r', encoding='utf-8') as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row)

File python class (класс для работы с файлами в python)

{'name': 'Alice', 'age': '30', 'score': '95'}
{'name': 'Bob', 'age': '25', 'score': '88'}

Python file utf 8 (кодировка utf-8 для файлов в python)

Типичные проблемы:

  • Разделитель по умолчанию запятая – если данные содержат запятые, следует использовать delimiter=';' или экранирование.
  • Числа читаются как строки – требуется приведение типов вручную.
  • Разные кодировки – указывать encoding.

Применение: экспорт/импорт из Excel, простые логи, списки контактов.


Как организовать конфигурацию приложения?

Для INI‑файлов используется configparser, для YAML – сторонняя библиотека pyyaml.

ConfigParser (INI):

import configparser

config = configparser.ConfigParser()
config['DEFAULT'] = {'debug': 'false', 'port': '8080'}
config['DATABASE'] = {'host': 'localhost', 'user': 'admin'}

with open('config.ini', 'w') as f:
    config.write(f)

config.read('config.ini')
print(config['DATABASE']['host'])

Python config files (конфигурационные файлы в python)

localhost

Python copy file (копирование файла в python)

Ошибки:

  • Отсутствие секции – KeyError. Проверять через config.has_section.
  • Параметры читаются как строки – нужны явные преобразования через getint(), getboolean().

YAML (с pyyaml):

import yaml

config = {
    'server': {'host': '0.0.0.0', 'port': 5000},
    'database': {'url': 'sqlite:///app.db'}
}

with open('config.yaml', 'w') as f:
    yaml.dump(config, f, default_flow_style=False)

with open('config.yaml') as f:
    loaded = yaml.safe_load(f)
print(loaded)

Python log file (логирование в файл в python)

{'server': {'host': '0.0.0.0', 'port': 5000}, 'database': {'url': 'sqlite:///app.db'}}

Python file methods (методы работы с файлами в python)

Проблемы YAML:

  • Использование yaml.load без аргумента Loader может быть опасным. Всегда применять yaml.safe_load.
  • Ошибки синтаксиса (неправильные отступы) – yaml.scanner.ScannerError.

Использование: конфигурационные файлы приложений, DevOps‑контексты.


Как хранить данные в формате ключ‑значение без базы данных?

Модуль shelve предоставляет постоянное хранилище, подобное словарю. Данные сериализуются через pickle.

import shelve

with shelve.open('mydata') as db:
    db['user'] = {'name': 'Alice', 'age': 30}
    db['scores'] = [100, 90]

with shelve.open('mydata') as db:
    print(db['user'])

File models in python (модели файлов в python)

{'name': 'Alice', 'age': 30}

Ошибки:

  • Файл shelve блокируется – не рекомендуется при конкурентном доступе.
  • Создаются вспомогательные файлы (.dat, .dir, .bak), их нельзя удалять отдельно.
  • Зависимость от pickle – те же риски безопасности.

Применение: кеш, простые БД с малым объёмом данных, локальное состояние приложения.

- Python file position (позиционирование в файле python)
- Python line find (поиск строки в файле python)
- Python csv file (работа с csv файлами в python)

Расширенные примеры хранения данных

Пример
import json, os, datetime
from pathlib import Path

# Пример: сохранение нескольких записей с метками времени
data = [
    {'id': 1, 'name': 'Alice', 'timestamp': datetime.datetime.now().isoformat()},
    {'id': 2, 'name': 'Bob', 'timestamp': '2024-01-15T10:30:00'}
]

def save_json(filename, data, mode='w'):
    try:
        # Преобразование всех datetime в строки
        serializable = []
        for item in data:
            item_copy = item.copy()
            for k, v in item_copy.items():
                if isinstance(v, datetime.datetime):
                    item_copy[k] = v.isoformat()
            serializable.append(item_copy)
        
        with open(filename, mode, encoding='utf-8') as f:
            json.dump(serializable, f, ensure_ascii=False, indent=2)
        return True
    except (IOError, TypeError) as e:
        print(f'Ошибка записи: {e}')
        return False

def load_json(filename):
    if not Path(filename).exists():
        return []
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            return json.load(f)
    except json.JSONDecodeError as e:
        print(f'Ошибка разбора JSON: {e}')
        return []

# Сохраняем
if save_json('records.json', data):
    print('Данные сохранены')

# Добавляем новую запись
existing = load_json('records.json')
existing.append({'id': 3, 'name': 'Charlie', 'timestamp': '2024-02-20T12:00:00'})
save_json('records.json', existing)  # перезапись

# Читаем и выводим первое значение
records = load_json('records.json')
print(records[0]['name'])
Данные сохранены
Alice
Пример
# Пример: работа с большим файлом – чтение построчно без загрузки всего файла
# Потенциально для JSON Lines (.jsonl) – каждая строка – отдельный JSON

def append_json_line(filename, obj):
    with open(filename, 'a', encoding='utf-8') as f:
        json.dump(obj, f, ensure_ascii=False)
        f.write('\n')

def read_json_lines(filename):
    with open(filename, 'r', encoding='utf-8') as f:
        for line in f:
            if line.strip():
                yield json.loads(line)

# Запишем три строки
append_json_line('big_data.jsonl', {'a': 1})
append_json_line('big_data.jsonl', {'b': 2})
append_json_line('big_data.jsonl', {'c': 3})

# Прочитаем построчно
for record in read_json_lines('big_data.jsonl'):
    print(record)
{'a': 1}
{'b': 2}
{'c': 3}
Пример
# Пример: сериализация сложных структур с помощью custom encoder (JSON)
from json import JSONEncoder

class Person:
    def __init__(self, name, birth):
        self.name = name
        self.birth = birth  # datetime.date
    def to_json(self):
        return {'__person__': True, 'name': self.name, 'birth': self.birth.isoformat()}

class PersonEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Person):
            return obj.to_json()
        return super().default(obj)

p = Person('Alice', datetime.date(1995, 5, 10))
json_str = json.dumps(p, cls=PersonEncoder, indent=2)
print(json_str)

# Загрузка с проверкой
def decode_person(dct):
    if '__person__' in dct:
        return Person(dct['name'], datetime.date.fromisoformat(dct['birth']))
    return dct

loaded = json.loads(json_str, object_hook=decode_person)
print(loaded.name, loaded.birth)
{
  "__person__": true,
  "name": "Alice",
  "birth": "1995-05-10"
}
Alice 1995-05-10
Пример
# Пример: использование pickle для передачи состояния между запусками (с проверкой версии)
import pickle, sys

class GameState:
    def __init__(self, level, lives):
        self.__version__ = 2  # версия для совместимости
        self.level = level
        self.lives = lives
    
    def upgrade(self):
        # при необходимости обновление старых объектов
        pass

state = GameState(5, 3)
with open('savegame.pkl', 'wb') as f:
    pickle.dump(state, f)

# Загрузка с проверкой версии
with open('savegame.pkl', 'rb') as f:
    loaded = pickle.load(f)
if loaded.__version__ != 2:
    loaded.upgrade()
print(loaded.level, loaded.lives)
5 3
Пример
# Пример: сохранение многомерного массива с помощью numpy (если разрешён сторонний модуль)
import numpy as np

arr = np.random.rand(3, 4)
np.savetxt('matrix.csv', arr, delimiter=',', fmt='%.6f')
loaded_arr = np.loadtxt('matrix.csv', delimiter=',')
print(loaded_arr.shape)
(3, 4)

В этом разделе приведены нестандартные приёмы: кастомный кодировщик для JSON, работа с JSON Lines для потоковой обработки, проверка версий в pickle, интеграция с numpy. Каждый из них решает специфическую задачу, которая возникает в реальных проектах.

Хранение данных в файлах (persistent storage) в Python - comments

En
Python file storage (python)