Работа с CSV-файлами
В реальной жизни данные редко хранятся в виде сплошного текста. Чаще всего они представляют собой структурированные данные, например, таблицы или словари, поэтому для их эффективного хранения требуется не просто текстовый формат .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
Здесь первая строка содержит заголовки столбцов, а каждая следующая строка – их значения.
Такой файл можно открыть в Python, но если выполнять операции чтения и записи с помощью стандартных методов file.read() и file.write(), то нужно учитывать некоторые особенности, например:
- Если в одном из полей содержится символ-разделитель (запятая), то её нужно экранировать, то есть заключить внутри двойных кавычек (
","). - Если само поле должно содержать кавычку, ее нужно экранировать с помощью ещё одной кавычки (
""). - На разных операционных системах по-разному обрабатывается символ перевода строки (
\nили\r\n).
Модуль CSV
Модуль csv облегчает работу с CSV-файлами, так как он предоставляет специальные объекты чтения и записи, которые автоматически обрабатывают все правила форматирования (кавычки, разделители, переводы строки), позволяя работать с данными в CSV-файле в удобном формате списков или словарей.
Перед началом работы модуль csv следует импортировать:
import csv
Объекты чтения
Объект чтения представляет собой итератор, который берёт открытый файл, разбирает его построчно согласно правилам CSV (обрабатывая кавычки, разделители и переводы строки) и возвращает данные в виде удобных структур Python (списков или словарей).
Для создания объекта чтения необходимо передать файловый объект, то есть открытый CSV-файл, функции csv.reader() или конструктору класса csv.DictReader. В первом случае объект чтения позволит работать с содержимым файла как со списком, а во втором – как со словарём.
|
Характеристика |
Функция |
Класс |
|---|---|---|
|
Параметры |
|
|
|
Возвращаемое значение |
Объект чтения |
|
|
Выходной тип данных |
Список |
Словарь |
|
Обработка заголовков |
Ручная ( |
Автоматическая (используются как ключи) |
|
Доступ к данным |
По индексу |
По ключу |
При открытии 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 для записи в него словарей.
|
Характеристика |
Функция |
Класс |
|---|---|---|
|
Параметры |
|
|
|
Возвращаемое значение |
Объект записи |
|
|
Записываемый тип данных |
Список |
Словарь |
|
Требование заголовков |
Нет |
Да |
|
Порядок столбцов |
Зависит от порядка элементов в списке |
Определяется списком fieldnames |
Запись одной строки
Метод writer.writerow() записывает в CSV-файл одну строку. Она должна представлять собой список (для функции csv.writer()) или словарь (для класса csv.DictWriter).
|
Функция |
|
|
Описание |
Записывает список или словарь row в файл, для которого был создан объект чтения writer |
|
Параметры |
|
|
Возвращаемое значение |
|
При создании объекта записи функции 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.
|
Метод |
|
|
Описание |
Записывает список |
|
Возвращаемое значение |
|
Этот метод записывает значения из списка 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).
|
Функция |
|
|
Описание |
Записывает список списков (или словарей) |
|
Параметры |
|
|
Возвращаемое значение |
|
Давайте запишем несколько дополнительных строк в файл 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)
# Вывод: ['Толстой Лев Николаевич', 'Достоевский Федор Михайлович', 'Чехов Антон Павлович', 'Пушкин Александр Сергеевич', 'Гоголь Николай Васильевич', 'Бунин Иван Алексеевич']
0 комментариев