Learning Platform
Глоссарий Troubleshooting
Урок 06.01 · 22 мин
Начальный
csvjsonjsonlndjsontext-formats

Зачем вообще говорить про CSV

CSV (Comma-Separated Values) — это самый старый и самый распространённый формат табличных данных. Ему примерно столько же лет, сколько и BASIC: первая публикация спецификации — 1972 год, IBM Fortran. С тех пор каждый аналитик хоть раз получал на почту файл report_2026_q1.csv с просьбой “посмотреть, что не так”. И каждый раз “не так” — это какой-нибудь один странный символ.

В мире data engineering текстовые форматы (CSV, JSON, JSONL) — это то, с чем мы сталкиваемся на границах систем: выгрузки из старых ERP, дампы из веб-форм, REST API ответы, экспорт из Google Sheets. Бинарные форматы (Parquet, Avro, ORC) живут глубоко внутри пайплайнов и хранилищ. Текстовые — на входе и выходе.

Эта пара (текст vs бинарь) — фундаментальное разделение. Текст читается человеком, отлаживается глазами, открывается в любом редакторе. Бинарь — компактный, типизированный, быстрый. Углубление по бинарным форматам — в курсе storage-formats; здесь мы фиксируем базу.

CSV изнутри: кодировки, разделители, edge-cases JSON: формат, кодирование и ограничения для аналитики CSV в Python: дьявол в деталях парсинга

Структура CSV

CSV — это plain-text файл, где каждая строка — одна запись, поля разделены запятой, первая строка обычно содержит заголовки колонок.

order_id,customer_id,amount,currency,created_at
1001,42,150.00,USD,2026-05-17T10:32:00Z
1002,43,2300.50,EUR,2026-05-17T10:35:12Z
1003,42,99.99,USD,2026-05-17T11:00:00Z

Кажется простым. На практике в CSV кроется десяток подводных камней, и любая команда data engineering хоть раз ловила баг “у нас 1.2 млн строк, а должно быть 1.0 млн — что-то не парсится”.

Разделители

Запятая — это convention, но не закон. Разделителем может быть точка с запятой (Excel в локалях, где запятая — десятичный разделитель: Германия, Россия, Франция), табуляция (TSV, часто называют тот же CSV), пайп |, тильда. RFC 4180 фиксирует запятую как стандарт, но в реальности парсер должен уметь принять любой delimiter параметром.

import pandas as pd

df = pd.read_csv("orders.csv", sep=";")  # европейский CSV
df = pd.read_csv("orders.tsv", sep="\t")  # TSV

Кавычки и escape

Что если в поле есть сама запятая? Например, адрес "Москва, ул. Тверская, 7". Тогда поле оборачивается в кавычки. А если в поле есть кавычка? Тогда она удваивается: "Он сказал ""привет""".

order_id,customer_name,address
1001,"Smith, John","NYC, 5th Ave"
1002,"O""Brien","Boston, MA"

Этот механизм — quote/escape — самый частый источник багов. Аналитик выгружает CSV без кавычек, в адресе есть запятая, парсер ломается через пять часов работы, никто не понимает почему. Защита: всегда указывать quotechar='"' и escapechar явно, валидировать перед записью.

Encoding

CSV не несёт информации о кодировке. Файл может быть UTF-8, UTF-8 с BOM, Windows-1251, CP1252, ISO-8859-1. На macOS откроется как UTF-8, на старом Windows — как CP1252, и кириллица превратится в кракозябры. Правило: всегда писать UTF-8, всегда читать с явным указанием encoding="utf-8", валидировать первые байты.

Типы

В CSV всё — строка. Число 42 и строка "42" неразличимы. Дата 2026-05-17 — это строка, которую парсер должен интерпретировать. Пустое поле может быть пустой строкой, NULL, или значением “NA”. Никакого type system в CSV нет, всё на совести читающего.

Жизненный цикл CSV-файла

От источника до целевой системы, и где обычно ломается

Источник: ERP, Google Sheets, экспорт из CRM, выгрузка из веб-формы. Здесь задаётся encoding, разделитель, формат дат — и часто всё это не документировано
Парсер: pandas, csv.reader, Spark spark.read.csv. Главный риск — auto-detect разделителя и encoding падает на нестандартных файлах
Цель: warehouse (Snowflake/BigQuery COPY INTO), data lake (S3 + Parquet), Postgres COPY FROM. На границе типы должны быть приведены явно

Плюсы CSV

CSV невозможно вытеснить, и это не баг, а фича. Причины:

  1. Универсальность. Любая программа открывает CSV — от Notepad до SAS до Excel до Python. Это lingua franca.
  2. Прозрачность. Можно открыть глазами, увидеть проблему, поправить руками.
  3. Простота генерации. print("a,b,c") — уже CSV. Не нужны библиотеки.
  4. Streaming-friendly. Файл читается построчно, без необходимости загружать целиком — критично для больших файлов.
  5. Diff-friendly. В git CSV меняется построчно, можно ревьюить изменения.

Минусы CSV

  1. Нет схемы. Типы теряются, схема живёт в README или в голове.
  2. Нет компрессии “из коробки”. Компрессию навешивают сверху (.csv.gz), но это всё равно текст внутри.
  3. Дорого парсить. Каждое число конвертируется из строки в int/float — это десятки наносекунд на значение, миллиарды значений = минуты.
  4. Нет nested структур. Массив или объект внутри ячейки — нет такого механизма, кроме как воткнуть JSON в строку.
  5. Чувствительность к разделителям, кавычкам, encoding. Один странный символ — и парсер падает или, что хуже, молча сдвигает колонки.

JSON: для вложенных структур

JSON (JavaScript Object Notation) появился в начале 2000-х как формат сериализации для JavaScript и быстро стал стандартом для REST API. В отличие от CSV, JSON изначально умеет вложенность.

{
  "order_id": 1001,
  "customer": {
    "id": 42,
    "name": "Smith, John",
    "addresses": [
      {"type": "billing", "city": "NYC"},
      {"type": "shipping", "city": "Boston"}
    ]
  },
  "items": [
    {"sku": "ABC", "qty": 2, "price": 75.00},
    {"sku": "XYZ", "qty": 1, "price": 0.00}
  ]
}

JSON хорош для API ответов и для документ-ориентированных данных (заказ с item-ами, пользователь с настройками). Плох для табличных выгрузок: оверхэд на ключи в каждой записи раздувает файл в 3-5 раз против CSV.

Типы в JSON более выразительные: число, строка, булево, null, массив, объект. Но даты — снова просто строка, и формат даты не нормирован спецификацией.

JSONL / NDJSON: JSON для больших данных

Классический JSON — это один большой объект или массив. Прочитать 100 GB JSON-файл нельзя — нужно загрузить всё в память. Решение: JSON Lines (он же NDJSON — Newline Delimited JSON). Каждая строка — самостоятельный JSON-объект.

{"order_id":1001,"amount":150.00,"customer":{"id":42}}
{"order_id":1002,"amount":2300.50,"customer":{"id":43}}
{"order_id":1003,"amount":99.99,"customer":{"id":42}}

JSONL читается построчно, как CSV — каждая строка парсится независимо. Это любимый формат для логов (один event = одна строка), для дампов из NoSQL баз, для streaming-выгрузок из Kafka. Большинство data lake тулов (Spark, DuckDB, Athena) умеют читать JSONL напрямую.

TIP

Если приходит JSON-выгрузка размером больше 100 MB — почти всегда стоит сразу переконвертировать в JSONL. Чтение и парсинг ускорятся в разы, потому что не нужно держать весь файл в памяти.

Когда что выбирать

CSV vs JSON vs JSONL — простая декомпозиция

Берите CSV для табличного, JSONL для логов и nested, JSON только для API

CSV
Табличное, плоское
Excel, отчёты, выгрузкиCSV: табличные данные с фиксированной схемой, обмен между системами, выгрузки в Excel, ad-hoc отчёты. Если данные плоские — это CSV
JSON
Одиночные документы
API, конфигиJSON: одиночные API-ответы, конфиги, метаданные. НЕ для больших датасетов — невозможно стримить
JSONL
События, логи, nested
Streaming-friendlyJSONL/NDJSON: логи, события, nested данные в большом объёме. Streaming-friendly, поддерживается Spark/DuckDB/Athena напрямую

Реальный пример: пайплайн обработки

Типичная задача: каждый день получаем CSV от партнёра с заказами, нужно сложить в data lake.

import pandas as pd

# 1. Читаем CSV с явными настройками
df = pd.read_csv(
    "partner_orders_2026_05_17.csv",
    sep=",",
    encoding="utf-8",
    quotechar='"',
    dtype={"order_id": "int64", "customer_id": "int64"},
    parse_dates=["created_at"]
)

# 2. Валидируем — нет ли пустых ключевых полей
assert df["order_id"].notna().all(), "Found null order_id"

# 3. Пишем в Parquet для long-term storage
df.to_parquet("s3://lake/orders/dt=2026-05-17/data.parquet")

CSV здесь — это формат транспорта: пришёл, спарсили, сложили в нормальное хранилище (Parquet). Долгосрочно CSV держать не нужно — он дорог и хрупок.

WARNING

Никогда не используйте CSV как формат хранения данных в lake/warehouse. Только как формат обмена. Внутри пайплайна — Parquet, ORC, Avro. CSV умирает на больших объёмах: миллиард строк в CSV — это десятки минут парсинга, в Parquet — секунды.

Особенности обработки в больших инструментах

Spark: spark.read.csv("path", header=True, inferSchema=True) — но inferSchema сканирует данные дважды (один раз для типов, второй для чтения). На больших файлах лучше задать схему явно.

DuckDB: SELECT * FROM read_csv_auto('file.csv') — лучший в классе auto-detect, обрабатывает кавычки, encoding, дедукцию типов.

ClickHouse: INSERT INTO table FROM INFILE 'file.csv' FORMAT CSVWithNames — нативный быстрый импорт.

Snowflake / BigQuery: COPY INTO ... FROM @stage с явным указанием file_format. Промахи с разделителем/encoding — главная причина failed COPY.

Попробуй сам

  1. Возьми любой CSV-файл с заказами (можно сгенерировать в Excel). Открой его в текстовом редакторе. Посмотри на сырой формат — кавычки, разделители, encoding (попробуй сохранить файл в Windows-1251 и открыть в редакторе с UTF-8).
  2. Напиши Python-скрипт, который читает CSV и выводит для каждой колонки выведенный тип (int, float, string, date). Подумай, как ты бы интерпретировал колонку, где 99% значений — числа, но есть пара строковых.
  3. Сконвертируй JSON-файл (любой REST API ответ) в JSONL: одна запись на строку. Сравни размеры файлов.
  4. Загрузи JSONL в DuckDB: SELECT * FROM read_json_auto('file.jsonl'). Посмотри, как DuckDB разворачивает nested структуры в колонки.
Проверка знанийKnowledge check
Партнёр прислал CSV-файл размером 5 GB с заказами. При попытке распарсить pandas падает с ошибкой "expected 7 fields, saw 9" на середине файла. В чём наиболее вероятная причина и как защититься?
ОтветAnswer
Причина — в одной из ячеек есть запятая (разделитель), но поле не обёрнуто в кавычки. Парсер видит две лишние "колонки" и падает. Защита: 1) всегда требовать от источника обёртки строковых полей в кавычки (quoting=QUOTE_ALL при записи); 2) при чтении передавать quotechar и escapechar явно; 3) валидировать перед записью — простой счётчик разделителей в каждой строке должен совпадать; 4) использовать форматы со встроенной схемой (Parquet/Avro) для больших данных вместо CSV.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Команда выгружает CSV из ERP, на ноуте macOS файл открывается корректно, на сервере (Linux) кириллица превращается в кракозябры. В чём причина и как защититься?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 3