В реальной жизни данные редко хранятся в виде сплошного текста. Чаще всего они представляют собой структурированные данные, например, таблицы или словари, поэтому для их эффективного хранения требуется не просто текстовый формат .txt, а более специализированные форматы, устанавливающие строгие правила для организации данных. Например, для хранения табличных данных часто используются CSV-файлы (от англ. Comma-Separated Values – значения, разделённые запятыми).

Стандартная библиотека Python предоставляет набор модулей, позволяющий работать с файлами различных форматов, в том числе, с CSV-файлами. Для этого предназначен модуль csv, который берёт на себя всю работу по кодированию, декодированию и обработке специальных символов, позволяя программисту сосредоточиться на работе с данными.

Особенности CSV-файлов

CSV-файл представляет собой обычный текстовый файл для представления табличных данных, структура которого подчиняется строгим правилам форматирования. В таком файле каждая строка соответствует одной строке в таблице, а значения, составляющие столбцы, разделены специальным символом-разделителем (англ. – delimiter), чаще всего запятой.

Давайте создадим новый CSV-файл students.csv со следующим содержимым:

ID,ФИО,Предмет,Оценка
101,Шишкин И.И.,Живопись,5
102,Репин И.Е.,Графика,4
103,Суриков В.И.,Пейзаж,5
104,Малевич К.С.,Архитектура,3
105,Серов В.А.,Живопись,4
106,Левитан И.И.,Пейзаж,5

Здесь первая строка содержит заголовки столбцов, а каждая следующая строка – их значения.

Image Gallery

CSV-файл и соответствующая ему таблица

Такой файл можно открыть в Python, но если выполнять операции чтения и записи с помощью стандартных методов file.read() и file.write(), то нужно учитывать некоторые особенности, например:

  • Если в одном из полей содержится символ-разделитель (запятая), то её нужно экранировать, то есть заключить внутри двойных кавычек (",").
  • Если само поле должно содержать кавычку, ее нужно экранировать с помощью ещё одной кавычки ("").
  • На разных операционных системах по-разному обрабатывается символ перевода строки (\n или \r\n).

Модуль CSV

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

Перед началом работы модуль csv следует импортировать:

import csv

Объекты чтения

Объект чтения представляет собой итератор, который берёт открытый файл, разбирает его построчно согласно правилам CSV (обрабатывая кавычки, разделители и переводы строки) и возвращает данные в виде удобных структур Python (списков или словарей).

Для создания объекта чтения необходимо передать файловый объект, то есть открытый CSV-файл, функции csv.reader() или конструктору класса csv.DictReader. В первом случае объект чтения позволит работать с содержимым файла как со списком, а во втором – как со словарём.

Создание объектов чтения

Характеристика

Функция csv.reader(file)

Класс csv.DictReader(file)

Параметры

  • file – файловый объект

Возвращаемое значение

Объект чтения

Выходной тип данных

Список

Словарь

Обработка заголовков

Ручная (next())

Автоматическая (используются как ключи)

Доступ к данным

По индексу

По ключу

При открытии CSV-файла функции open() важно передать именованный аргумент newline="". Это необходимо для того, чтобы предотвратить появление лишних пустых строк из-за различий в обработке окончаний строк между операционными системами. Например, Windows автоматически преобразует символ \n в два символа \r\n, но аргумент newline="" отключает встроенную обработку, позволяя модулю csv самому обрабатывать эти символы.

Чтение данных в виде списков

Давайте продолжим работу с файлом students.csv, откроем его и используем функцию csv.reader() для создания объекта чтения:

with open("students.csv", "r", newline="", encoding="utf-8") as students:
    csv_reader = csv.reader(students)

Такой объект является итератором, поэтому его можно перебрать в цикле for. В случае использования функции csv_reader() каждая прочитанная строка представляет собой список.

Если первая строка CSV-файла содержит заголовки, то их можно вручную прочитать с помощью функции next(), после чего цикл for будет перебирать все строки после заголовков:

with open("students.csv", "r", newline="", encoding="utf-8") as students:
    csv_reader = csv.reader(students)
    
    header = next(csv_reader)
    print(f"Заголовки: {header}")
    # Вывод: Заголовки: ['ID', 'ФИО', 'Предмет', 'Оценка']
    
    for row in csv_reader:
        print(row[1], row[2], row[3], sep=" - ")
   # Вывод: Шишкин И.И. - Живопись - 5
   # Вывод: Репин И.Е. - Графика - 4
   # Вывод: Суриков В.И. - Пейзаж - 5
   # Вывод: Малевич К.С. - Архитектура - 3
   # Вывод: Серов В.А. - Живопись - 4
   # Вывод: Левитан И.И. - Пейзаж - 5

Чтение данных в виде словарей

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

Такой способ является более предпочтительным для структурированных данных, поскольку он автоматически использует первую строку файла в качестве ключей словаря и начинает возвращать данные со второй строки:

with open("students.csv", "r", newline="", encoding="utf-8") as students:
    csv_reader_dict = csv.DictReader(students)
    for row in csv_reader_dict:
        print(f"ФИО: {row['ФИО']} - оценка: {row['Оценка']}")
    # Вывод: ФИО: Шишкин И.И. - оценка: 5
    # Вывод: ФИО: Репин И.Е. - оценка: 4
    # Вывод: ФИО: Суриков В.И. - оценка: 5
    # Вывод: ФИО: Малевич К.С. - оценка: 3
    # Вывод: ФИО: Серов В.А. - оценка: 4
    # Вывод: ФИО: Левитан И.И. - оценка: 5

Объекты записи

Объект записи используется для записи данных в CSV-файл с помощью специальных методов. Также как и объект чтения, его можно создать двумя способами: передать открытый CSV-файл функции csv.writer() для записи в него списков значений или передать его конструктору класса csv.DictWriter для записи в него словарей.

Сравнение объектов записи

Характеристика

Функция csv.writer(file)

Класс csv.DictWriter(file, fieldnames)

Параметры

  • file – файловый объект
  • file – файловый объект
  • fieldnames – список строк с заголовками столбцов

Возвращаемое значение

Объект записи

Записываемый тип данных

Список

Словарь

Требование заголовков

Нет

Да

Порядок столбцов

Зависит от порядка элементов в списке

Определяется списком fieldnames

Запись одной строки

Метод writer.writerow() записывает в CSV-файл одну строку. Она должна представлять собой список (для функции csv.writer()) или словарь (для класса csv.DictWriter).

Функция

writer.writerow(row)

Описание

Записывает список или словарь row в файл, для которого был создан объект чтения writer

Параметры

  • row – список или словарь, представляющий одну строку данных

Возвращаемое значение

None

При создании объекта записи функции csv.writer() достаточно передать файловый объект, но класс csv.DictWriter требует дополнительный параметр fieldnames с названиями столбцов CSV-файла:

with open("students.csv", "a+", newline="", encoding="utf-8") as students:
    # Записываем одну строку как список
    csv_writer = csv.writer(students)
    student_107 = ["107", "Саврасов А.К.", "Пейзаж", 5]
    csv_writer.writerow(student_107)
    
    # Записываем одну строку как словарь
    columns = ["ID", "ФИО", "Предмет", "Оценка"]
    csv_writer_dict = csv.DictWriter(students, fieldnames=columns)
    students_108 = {
        "ID": "108", 
        "ФИО": "Верещагин В.В.", 
        "Предмет": "Скульптура", 
        "Оценка": 4
    }
    csv_writer_dict.writerow(students_108)

    # Читаем файл
    students.seek(0)  # Возвращаем указатель в начало файла
    csv_reader = csv.reader(students)
    for row in csv_reader:
        print(row)
    # ...
    # Вывод: ['107', 'Саврасов А.К.', 'Пейзаж', '5']
    # Вывод: ['108', 'Верещагин В.В.', 'Скульптура', '4']

Здесь в файл students.csv были записаны две новые строки как список и как словарь.

Значения словаря записываются по ключу, в то время как элементы списка записываются в том порядке, в котором были перечислены, поэтому в списке fieldsname важен порядок названий столбцов, так как они определяют их порядок в выходном CSV-файле. 

Также класс csv.DictWriter предоставляет дополнительный метод для записи в CSV-файл списка fieldnames.

Метод

writer.writeheader()

Описание

Записывает список fieldnames в файл, для которого был создан объект записи writer

Возвращаемое значение

None

Этот метод записывает значения из списка fieldnames в виде обычной строки от текущей позиции указателя:

with open("students.csv", "a+", newline="", encoding="utf-8") as students:
    columns = ["ФИО", "ID", "Предмет", "Оценка"]
    csv_writer_dict = csv.DictWriter(students, fieldnames=columns)
    csv_writer_dict.writeheader()

    # Читаем файл
    students.seek(0)  # Возвращаем указатель в начало файла
    csv_reader = csv.reader(students)
    for row in csv_reader:
        print(row)
    # ...
    # Вывод: ['ФИО', 'ID', 'Предмет', 'Оценка']

Запись нескольких строк

Метод writer.writerow() позволяет записать сразу несколько строк, которые должны представлять собой список списков (для функции csv.writer()) или список словарей (для класса csv.DictWriter).

Функция

writer.writerows(rows)

Описание

Записывает список списков (или словарей) rows в файл, для которого был создан объект чтения writer

Параметры

  • rows – список списков (или словарей) со строками данных

Возвращаемое значение

None

Давайте запишем несколько дополнительных строк в файл students.csv:

with open("students.csv", "a+", newline="", encoding="utf-8") as students:
    # Записываем несколько строк как список списков
    csv_writer = csv.writer(students)
    students_data = [
        ["109", "Врубель М.А.", "Графика", 5],
        ["110", "Айвазовский И.К.", "Живопись", 5]
    ]
    csv_writer.writerows(students_data)
    
    # Записываем несколько строк как список словарей
    columns = ["ID", "ФИО", "Предмет", "Оценка"]
    csv_writer_dict = csv.DictWriter(students, fieldnames=columns)
    extra_students_data = [
        {
            "ФИО": "Поленов В.Д.", 
            "ID": "111", 
            "Предмет": "Скульптура", 
            "Оценка": 4
        },
        {
            "ID": "112", 
            "Оценка": 4,
            "ФИО": "Васнецов В.М.", 
            "Предмет": "Архитектура"
        }
    ]
    csv_writer_dict.writerows(extra_students_data)

    # Читаем файл
    students.seek(0)  # Возвращаем указатель в начало файла
    csv_reader = csv.reader(students)
    for row in csv_reader:
        print(row)
    # ...
    # Вывод: ['109', 'Врубель М.А.', 'Графика', '5']
    # Вывод: ['110', 'Айвазовский И.К.', 'Живопись', '5']
    # Вывод: ['111', 'Поленов В.Д.', 'Скульптура', '4']
    # Вывод: ['112', 'Васнецов В.М.', 'Архитектура', '4']

Несмотря на то, что в словарях ключи записаны не в том порядке, в котором идут столбцы, они всё равно записываются в верном порядке, который был задан в списке fieldnames.

Примеры

Пример 1. Расчёт суммарного населения в каждом округе

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

import csv

cities_data = [
    {"Город": "Москва", "Округ": "Центральный", "Население": 13000000},
    {"Город": "Казань", "Округ": "Приволжский", "Население": 1250000},
    {"Город": "Самара", "Округ": "Приволжский", "Население": 1150000},
    {"Город": "Тверь", "Округ": "Центральный", "Население": 400000},
]

# Записываем словарь в CSV-файл
with open("city_population.csv", "w", newline="", encoding="utf-8") as city_population_file:
    header = list(cities_data[0].keys())
    writer = csv.DictWriter(city_population_file, fieldnames=header)
    writer.writeheader()
    writer.writerows(cities_data)

# Агрегируем данные
district_population = {}
with open("city_population.csv", "r", newline="", encoding="utf-8") as city_population_file:
    reader = csv.DictReader(city_population_file)
    for row in reader:
        district = row["Округ"] 
        pop = int(row["Население"])

        # Суммируем население по коругам
        district_population[district] = district_population.get(district, 0) + pop
        
for district, total_pop in district_population.items():
    print(f"{district} округ: {total_pop} человек")

Вывод:

Центральный округ: 13400000 человек
Приволжский округ: 2400000 человек

Пример 2. Транспонирование данных

Список initial_data содержит данные об измерении температуры и влажности на разные даты. В необработанном виде он сохраняется в файл sensor_reading_in.csv, а после транспонирования (даты становятся столбцами, а названия измерений – строками) – в файл sensor_reading_out.csv:

import csv

initial_data = [
    ["Дата", "Температура", "Влажность"],
    ["2025-11-01", 20.5, 60],
    ["2025-11-02", 21.0, 58],
    ["2025-11-03", 19.8, 62],
]

# Записываем исходные данные в CSV-файл
with open("sensor_readings_in.csv", "w", newline="", encoding="utf-8") as infile:
    writer = csv.writer(infile)
    writer.writerows(initial_data)

# Читаем содержимое этого файла
all_rows = []
with open("sensor_readings_in.csv", "r", newline="", encoding="utf-8") as infile:
    reader = csv.reader(infile)
    all_rows = list(reader)

# Транспонируем список списков
# zip(*all_rows) выполняет транспонирование (разворачивает строки в корте-жи)
transposed_rows = list(zip(*all_rows))

# Записываем транспонированные данные
with open("sensor_readings_out.csv", "w", newline="", encoding="utf-8") as outfile:
    writer = csv.writer(outfile)
    writer.writerows(transposed_rows)

for row in transposed_rows:
    print(f"{row}")

Вывод:

('Дата', '2025-11-01', '2025-11-02', '2025-11-03')
('Температура', '20.5', '21.0', '19.8')
('Влажность', '60', '58', '62') 

Пример 3. Форматирование номеров телефонов

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

Каждый номер телефона из этого файла обрабатывается с помощью функции normalize_phone(phone_str), которая удаляет из номера phone_str все нецифровые символы и добавляет к нему префикс +7. После этого данные с обработанными номерами телефонов записываются в новый файл clean_contacts.csv:

import csv

# Создаём файл с исходными данными
with open("raw_contacts.csv", "w+", newline="", encoding="utf-8") as raw_contacts_file:
    raw_contacts = [
        ["Имя", "Телефон", "Email"],
        ["Алексей", "8-900-123-45-67", "a@mail.com"],
        ["Мария", "+7(912) 987 65 43", "m@mail.com"],
        ["Денис", "905-111-22-33", "d@mail.com"],
    ]
    writer = csv.writer(raw_contacts_file)
    writer.writerows(raw_contacts)


def normalize_phone(phone_str: str) -> str:
    """Очищает телефонный номер и приводит его к формату +7XXXXXXXXXX.
    
    Параметры:
        phone_str: Неотформатированный номер телефона.
        
    Возвращает:
        Номер телефона, приведённый к формату +7XXXXXXXXXX.
    """
    digits = "".join(filter(str.isdigit, phone_str))
    
    if digits.startswith("8"):
        digits = "7" + digits[1:]
    elif not digits.startswith('7') and len(digits) == 10:
        # Предполагаем, что это 10-значный номер без 7/8
        digits = "7" + digits
        
    if len(digits) == 11:
        return f"+{digits}"
    return phone_str # Возвращаем как есть, если не удалось нормализовать


# Обрабатываем исходные данные
with open("raw_contacts.csv", "r", newline="", encoding="utf-8") as raw_contacts_file:
    cleaned_rows = []  # Список для обработанных строк
    reader = csv.reader(raw_contacts_file)
    header = next(reader)
    cleaned_rows.append(header) # Сохраняем заголовок
    
    # Индекс столбца "Телефон" - 1
    phone_index = header.index("Телефон") if "Телефон" in header else 1

    for row in reader:
        raw_phone = row[phone_index]
        row[phone_index] = normalize_phone(raw_phone)
        cleaned_rows.append(row)
        print(f"{raw_phone} -> {row[phone_index]}")

# Записываем обработанные данные в новый файл
with open("clean_contacts.csv", "w", newline="", encoding="utf-8") as clean_contacts_file:
    writer = csv.writer(clean_contacts_file)
    writer.writerows(cleaned_rows)

Вывод:

8-900-123-45-67 -> +79001234567
+7(912) 987 65 43 -> +79129876543
905-111-22-33 -> +79051112233

Итоги

  • CSV-файл – это файл с расширением .csv для представления табличных данных, в котором каждая строка соответствует одной строке в таблице, а значения, составляющие столбцы, разделены специальным символом-разделителем, чаще всего запятой.
  • Модуль csv предоставляет специальные объекты чтения и записи для работы с CSV-файлами, позволяющие работать с данными в формате списков или словарей.
  • Объект чтения, созданный с помощью функции csv.reader() возвращает данные в виде списка, а с помощью класса csv.DictReader – в виде словаря.
  • Объект записи, созданный функцией csv.writer(), записывает в CSV-файл список, а классом csv.DictWriter – словарь.
  • Метод writer.writerow() записывает в CSV-файл список или словарь как одну строку.
  • Метод writer.writerow() записывает в CSV-файл список списков или список словарей как несколько строк.

Задания для самопроверки

1. Почему при работе с CSV-файлами не рекомендуется использовать стандартные методы чтения file.read() или file.readline()?

Стандартные методы чтения не учитывают структуру и специфичные правила форматирования CSV-файлов. Например, на разных операционных системах по-разному обрабатывается символ перевода строки (\n или \r\n).

2. Какой обязательный дополнительный параметр (кроме пути к файлу) должен быть передан конструктору класса csv.DictWriter? Для чего он нужен?

Параметр fieldnames – список с названиями столбцов CSV-файла, который используется в качестве ключей словаря. Порядок названий в списке fielnames определяет их порядок в выходном файле.

3. Создайте файл students.csv со следующим содержанием:

ФИО,Курс,Специальность,Средний балл
Толстой Лев Николаевич,3,Филология,4.8
Достоевский Федор Михайлович,2,История,4.5
Чехов Антон Павлович,4,Медицина,4.9
Пушкин Александр Сергеевич,1,Журналистика,4.2
Гоголь Николай Васильевич,3,Искусствоведение,4.6

Откройте этот файл в режиме чтения, создайте объект чтения с помощью функции csv.reader(), и выведите на экран список всех специальностей.

import csv

# Создание файла
data = [
    ["ФИО", "Курс", "Специальность", "Средний балл"],
    ["Толстой Лев Николаевич", 3, "Филология", 4.8],
    ["Достоевский Федор Михайлович", 2, "История", 4.5],
    ["Чехов Антон Павлович", 4, "Медицина", 4.9],
    ["Пушкин Александр Сергеевич", 1, "Журналистика", 4.2],
    ["Гоголь Николай Васильевич", 3, "Искусствоведение", 4.6],
]
with open("students.csv", "w", newline="", encoding="utf-8") as students:
    writer = csv.writer(students)
    writer.writerows(data)

# Чтение файла
with open("students.csv", "r", newline="", encoding="utf-8") as students:
    reader = csv.reader(students)
    # Пропускаем заголовок (первая строка)
    next(reader) 
    # Используем остальные строки
    specialties = [row[2] for row in reader]

print(specialties)
# Вывод: ['Филология', 'История', 'Медицина', 'Журналистика', 'Искусствоведение']

4. Откройте файл students.csv из задания 3 и создайте объект чтения с помощью класса csv.DictReader. Выведите на экран только ФИО и специальность для каждого студента.

import csv

with open("students.csv", "r", newline="", encoding="utf-8") as students:
    dict_reader = csv.DictReader(students)
    for student in dict_reader:
        # Обращаемся к данным по ключам (заголовкам столбцов)
        print(f"{student["ФИО"]}: {student["Специальность"]}")
    # Вывод: Толстой Лев Николаевич: Филология
    # Вывод: Достоевский Федор Михайлович: История
    # Вывод: Чехов Антон Павлович: Медицина
    # Вывод: Пушкин Александр Сергеевич: Журналистика
    # Вывод: Гоголь Николай Васильевич: Искусствоведение

5. Добавьте в конец файла students.csv из задания 3 новый список ["Бунин Иван Алексеевич", 3, "Лингвистика", 5]. Выведите на экран список всех ФИО студентов.

import csv

with open("students.csv", "a", newline="", encoding="utf-8") as students:
        writer = csv.writer(students)
        writer.writerow(["Бунин Иван Алексеевич", 3, "Лингвистика", 5])

with open("students.csv", "r", newline="", encoding="utf-8") as students:
    dict_reader = csv.DictReader(students)
    students = [student["ФИО"] for student in dict_reader]
    print(students)
    # Вывод: ['Толстой Лев Николаевич', 'Достоевский Федор Михайлович', 'Чехов Антон Павлович', 'Пушкин Александр Сергеевич', 'Гоголь Николай Васильевич', 'Бунин Иван Алексеевич']