CSV проще, чем кажется (и хитрее, чем ожидаешь)
CSV-файл — это просто текст: строки разделены переводами строк, поля — запятыми. Таков замысел. На практике натыкаешься на строки в кавычках с запятыми внутри, поля со встроенными переводами, разные региональные соглашения (запятая или точка с запятой), файлы из Excel с BOM — краевых случаев достаточно, чтобы писать свой line.split(",") почти всегда было ошибкой.
Модуль csv стандартной библиотеки обрабатывает всё это. Для маленьких и средних файлов тебе редко понадобится что-то ещё.
Чтение CSV через csv.reader
csv.reader выдаёт каждую строку списком строк:
import csv
with open("people.csv", newline="") as f:
reader = csv.reader(f)
for row in reader:
print(row)
Пара неочевидных деталей:
- Всегда передавай
newline=""вopen. Модуль csv сам следит за переводами строк; без этого на Windows появятся лишние пустые ряды. - Каждое значение — строка.
"42"останется строкой, пока не вызовешьint(...). У CSV нет типов. - Строка заголовка — такая же строка, как остальные. Если заголовки есть — либо вручную пропусти первую, либо переключайся на
DictReader.
Пропускаем заголовок
import csv
with open("people.csv", newline="") as f:
reader = csv.reader(f)
headers = next(reader) # pulls the first row out
for row in reader:
print(row)
next(reader) сдвигает итератор на один и возвращает ту строку.
Чтение как словарей через DictReader
csv.DictReader воспринимает первую строку как заголовки и отдаёт каждую следующую словарём:
import csv
with open("people.csv", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
print(row["name"], row["email"])
Это почти всегда то, что нужно. Имена колонок самодокументируются, и перестановка колонок в исходнике не ломает код.
Если заголовков нет — передай их явно: fieldnames=["name", "email", ...].
Запись CSV через csv.writer
csv.writer превращает ряды (списки) в строки CSV:
import csv
rows = [
["name", "age", "city"],
["Rosa", 30, "Lisbon"],
["Ada", 36, "London"],
]
with open("out.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(rows)
writerow(row) пишет один ряд; writerows(rows) — всё итерируемое сразу. Оба автоматически оборачивают поля в кавычки, когда внутри запятые, кавычки или переводы строк, — об этом думать не надо.
Запись словарей через DictWriter
Когда данные уже в виде словарей, DictWriter пропускает шаг «превратить в список»:
import csv
people = [
{"name": "Rosa", "age": 30, "city": "Lisbon"},
{"name": "Ada", "age": 36, "city": "London"},
]
with open("out.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "age", "city"])
writer.writeheader()
writer.writerows(people)
Аргумент fieldnames управляет и заголовком, и порядком колонок. Ключи словарей, не попавшие в fieldnames, молча выкидываются (или можно ругаться через extrasaction="raise").
Разные разделители и кавычки
Не в каждом «CSV» запятые. В европейских локалях часто ;, tab-separated файлы используют \t, в некоторых системах — |. Передай аргумент delimiter=:
import csv
with open("data.tsv", newline="") as f:
reader = csv.reader(f, delimiter="\t")
for row in reader:
print(row)
Для файлов с необычными правилами цитирования csv.register_dialect(...) позволяет один раз настроить и переиспользовать. Для большинства файлов хватает дефолтов плюс delimiter=.
Кодировка
CSV — это текст, у него есть кодировка. UTF-8 — современный дефолт; файлы из Excel на Windows иногда в cp1252 или с UTF-8 BOM. Будь явным:
with open("data.csv", newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
...
Видишь UnicodeDecodeError — файл не в той кодировке, что ты угадал. Обычные подозреваемые: utf-8-sig (учитывает Excel-овский BOM), cp1252, latin-1.
Превращаем строки CSV в полезные типы
Поскольку каждое значение приходит строкой, парсинг на тебе:
(StringIO позволяет запустить пример без реального файла — в настоящем коде ты бы сделал open(path).)
Для CSV со сложными типами (даты, nullable-числа, true/false в десяти разных написаниях) — подумай о pandas: у него конвенции встроены.
Когда тянуться к pandas
pandas.read_csv(path) возвращает DataFrame — правильную структуру с того момента, когда хочется:
- фильтровать ряды:
df[df["active"] == True] - агрегировать:
df.groupby("city")["age"].mean() - джойнить с другой таблицей
- записывать обратно с простым форматированием
import pandas as pd
df = pd.read_csv("people.csv")
adults = df[df["age"] >= 18]
adults.to_csv("adults.csv", index=False)
Для маленьких линейных чтений pandas — перебор, и это тяжёлая зависимость (поставь pip-ом в виртуальное окружение). Но для всего «в форме данных» — это инструмент, к которому большинство Python-аналитиков тянется.
Стриминг очень больших файлов
csv.reader уже ленив — читает ряд за раз. Оставляй его таким: итерируй (а не делай list(reader) заранее), и память остаётся плоской независимо от размера:
import csv
with open("huge.csv", newline="") as f:
reader = csv.DictReader(f)
error_count = 0
for row in reader:
if row["status"] == "error":
error_count += 1
print(f"Found {error_count} errors.")
Так спокойно тянется и 10 ГБ, и 10 КБ — лишь бы ты не копил ряды в список.
Несколько привычек
- Всегда передавай
newline=""вopenпри чтении и записи CSV. - Используй
DictReader/DictWriter, когда у файла есть заголовки — читается лучше, чем индексы. - Будь явным с кодировкой, особенно для файлов из Excel или неанглоязычных источников.
- Конвертируй типы прямо на этапе чтения, чтобы код ниже уже имел дело с нормальными значениями.
- Тянись к pandas, когда хочешь анализировать данные, а не просто двигать их.
Дальше
Теперь ты умеешь читать JSON и CSV. Последний реальный навык, который мы разберём, — тянуть данные по сети, и это следующая страница про HTTP-запросы через библиотеку requests.
Часто задаваемые вопросы
Как прочитать CSV-файл в Python?
Используй встроенный модуль csv. csv.reader даёт каждую строку списком строк; csv.DictReader использует первую строку как заголовки и выдаёт каждую следующую словарём. Открой файл с newline='', чтобы Python не калечил переводы строк: with open('data.csv', newline='') as f:.
Как записать CSV-файл в Python?
Сочетай csv.writer с файлом, открытым на запись с newline=''. Вызывай writer.writerow([...]) для одной строки или writer.writerows([[...], [...]]) для пачки. Для словарей — csv.DictWriter, он автоматически обрабатывает заголовки.
Модуль csv или pandas?
csv — для быстрого чтения и записи, обработки построчно и когда не хочется лишних зависимостей. pandas — когда нужно фильтровать, группировать или джойнить, либо файл настолько большой, что важна векторизация. Файлы они обрабатывают одни и те же; выбор — про то, что ты делаешь с данными после загрузки.
Почему у моего CSV на Windows пустые строки между рядами?
Ты открыл файл без newline=''. Модуль csv сам пишет терминаторы строк; без этого аргумента Python на Windows добавляет лишние. Всегда открывай CSV через open(path, newline='') и на чтение, и на запись.