CSV (Comma-Separated Values) — простейший табличный формат: строки файла соответствуют строкам таблицы, а значения внутри строки разделены запятой или другим символом. В нём приходят выгрузки из CRM и 1С, экспорт из Excel, отчёты рекламных систем и аналитики. Формат кажется тривиальным, и именно поэтому на нём чаще всего ошибаются. Эта статья входит в наш обзор парсинга документов и посвящена тому, как читать CSV правильно.
Почему «split по запятой» — это ошибка
Соблазн разобрать CSV вручную — line.split(",") — возникает у всех и почти всегда заканчивается багами. Дело в том, что в CSV есть экранирование: значение с запятой внутри берётся в кавычки, а кавычки внутри значения удваиваются.
sku,name,price
101,"Кофемолка, ручная",3490
102,"Чайник ""Ретро""",2190
Здесь "Кофемолка, ручная" — это одно значение, а не два, и Чайник "Ретро" содержит кавычки. Любой ручной split это сломает. Поэтому всегда используют готовый CSV-ридер — он знает про кавычки, экранирование и переносы строк внутри ячеек.
Разделители, кодировки и BOM
Несмотря на «comma» в названии, разделителем часто оказывается точка с запятой (;) — так Excel сохраняет файлы в локалях, где запятая это десятичный разделитель. Встречаются также табуляция (TSV) и вертикальная черта. Хороший парсер умеет определять разделитель автоматически или принимает его параметром.
Вторая постоянная проблема — кодировка. Файлы из старых учётных систем часто приходят в windows-1251, а не в UTF-8. Плюс файлы из Excel нередко начинаются с BOM (невидимая метка в начале), из-за которой имя первой колонки читается как \ufeffsku вместо sku. Лечится это указанием правильной кодировки при чтении (в Python — encoding="utf-8-sig", которая сама убирает BOM).
Python
В стандартной библиотеке есть модуль csv, которого достаточно в большинстве случаев. Удобнее всего DictReader — он отдаёт каждую строку как словарь по заголовкам.
import csv
with open("prices.csv", encoding="utf-8-sig", newline="") as f:
reader = csv.DictReader(f, delimiter=";")
for row in reader:
print(row["sku"], row["name"], row["price"])
Когда CSV большой или его нужно сразу анализировать — фильтровать, группировать, считать — берут pandas. Одна строка читает файл в таблицу (DataFrame).
import pandas as pd
df = pd.read_csv("prices.csv", sep=";", encoding="utf-8-sig")
expensive = df[df["price"] > 3000]
print(expensive[["sku", "name"]])
Pandas же удобен для конвертации: тот же DataFrame одной командой сохраняется обратно в CSV, Excel или JSON.
JavaScript / Node.js
В браузере и в Node самый популярный парсер — Papa Parse: он автоматически определяет разделитель, понимает заголовки и умеет работать с потоками.
const Papa = require("papaparse");
const fs = require("fs");
const file = fs.readFileSync("prices.csv", "utf-8");
const result = Papa.parse(file, { header: true, skipEmptyLines: true });
for (const row of result.data) {
console.log(row.sku, row.name, row.price);
}
Для серверных пайплайнов с большими файлами чаще берут csv-parse — потоковый парсер из экосистемы Node CSV.
PHP
Во встроенной функции fgetcsv уже заложена обработка кавычек, так что её достаточно для простых задач.
<?php
$f = fopen("prices.csv", "r");
$headers = fgetcsv($f, 0, ";");
while (($row = fgetcsv($f, 0, ";")) !== false) {
$record = array_combine($headers, $row);
echo $record["sku"] . " — " . $record["name"] . "\n";
}
fclose($f);
Для более удобной работы — с фильтрацией, выборкой колонок, конвертацией кодировки — используют библиотеку league/csv.
Go
В стандартной библиотеке Go есть пакет encoding/csv, которого хватает для большинства задач, включая нестандартный разделитель.
package main
import (
"encoding/csv"
"fmt"
"os"
)
func main() {
f, _ := os.Open("prices.csv")
defer f.Close()
r := csv.NewReader(f)
r.Comma = ';'
rows, _ := r.ReadAll()
for _, row := range rows[1:] { // пропускаем заголовок
fmt.Println(row[0], row[1], row[2])
}
}
Когда CSV перестаёт справляться
CSV хорош, пока данные плоские. Как только появляется вложенность, несколько таблиц в одном файле, форматирование, формулы или объединённые ячейки — это уже не задача CSV, а парсинг Excel. И наоборот: если исходник в Excel слишком сложен для прямого чтения, его часто сначала экспортируют в CSV и обрабатывают единообразно. А если CSV — это, по сути, выгрузка событий или строк журнала, посмотрите подходы из статей про парсинг TXT и парсинг логов.
Если выгрузки приходят с «плавающими» кодировками, разными разделителями от выгрузки к выгрузке или битыми кавычками — мы приведём их к единому виду и настроим стабильный парсинг с выгрузкой в нужную систему.