Чтение CSV в Python: DictReader, разделители и проверка строк

Чтение CSV в Python: DictReader, разделители и проверка строк
Содержание статьи

CSV-файл лучше читать модулем csv, а не делить строки через split. Разделитель может оказаться внутри кавычек, а одна запись может занимать несколько строк. DictReader сопоставляет значения с названиями столбцов, но проверку обязательных полей и чисел нужно написать отдельно.

В учебном примере Mentoring.digital прочитаем список материалов. Это разбор формата и проверки входных данных, а не готовый импорт в рабочую базу.

Сначала зафиксируйте формат файла

Сохраните inventory.csv в UTF-8. В нашем договорённом формате ровно два столбца, разделитель ;, количество является неотрицательным целым числом:

name;quantity
"Кабель; USB";3
Переходник;0

Точка с запятой в названии первого предмета находится внутри кавычек и не создаёт третий столбец. У CSV нет единственного обязательного разделителя. Если ваш источник использует запятую или другую кодировку, измените настройки под этот источник, а не перебирайте их до исчезновения ошибки.

Прочитайте записи и проверьте их контракт

import csv

def load_inventory(path):
    items = []
    with open(path, encoding="utf-8-sig", newline="") as stream:
        reader = csv.DictReader(stream, delimiter=";", strict=True)
        fields = reader.fieldnames or []
        if len(fields) != 2 or set(fields) != {"name", "quantity"}:
            raise ValueError("Expected name and quantity columns")

        for row in reader:
            line = reader.line_num
            if None in row or any(v is None for v in row.values()):
                raise ValueError(f"Wrong column count near line {line}")
            name = row["name"].strip()
            if not name:
                raise ValueError(f"Empty name near line {line}")
            try:
                quantity = int(row["quantity"])
            except ValueError as error:
                raise ValueError(f"Invalid quantity near line {line}") from error
            if quantity < 0:
                raise ValueError(f"Negative quantity near line {line}")
            items.append({"name": name, "quantity": quantity})
    return items

Вызов load_inventory("inventory.csv") возвращает список словарей. Для приведённого файла это названия «Кабель; USB» и «Переходник» с количествами 3 и 0. Исходник не изменяется, а при нарушении правил функция прекращает чтение с ошибкой.

newline="" оставляет обработку переводов строк модулю csv. Кодировка utf-8-sig принимает UTF-8 и убирает BOM в начале файла, если он присутствует. Это не универсальный способ читать любую кодировку: файл Windows-1251 нужно обрабатывать с его настоящей кодировкой.

В примере разрешён другой порядок двух столбцов, но не дополнительные или повторяющиеся заголовки. Названия сравниваются буквально. Если контракт допускает пробелы в заголовках или другой регистр, нормализацию нужно добавить явно и повторно проверить уникальность имён.

Почему одного DictReader недостаточно

При лишних значениях DictReader по умолчанию помещает остаток под ключ None. При недостающих значениях заполняет соответствующие поля значением None. Поэтому код отдельно проверяет форму строки, прежде чем вызывать strip или int.

Параметр strict=True помогает обнаруживать ошибки CSV-разметки, но не проверяет смысл данных. Отрицательное количество, пустое название и слово вместо числа остаются правилами нашего приложения. Преобразование int допускает окружающие пробелы и знак числа; если входной формат строже, дополните эту проверку.

reader.line_num сообщает число прочитанных физических строк. Для записи с переносом внутри кавычек это не порядковый номер записи. Поэтому сообщение указывает область файла, а не обещает точный номер бизнес-записи.

Какие случаи добавить в тесты

  • Обычные строки и разделитель внутри кавычек.
  • UTF-8 с BOM и без него.
  • Повторяющиеся заголовки, отсутствующая колонка и лишнее значение.
  • Пустое имя, отрицательное и нечисловое количество.
  • Корректный пустой набор: только строка заголовков.

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

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

Похожие статьи по данной теме