CSV to jeden z najprostszych i najpopularniejszych formatów przechowywania danych tabelarycznych. W artykule wyjaśniamy, jak działa CSV, jakie są jego ograniczenia i jak bezpiecznie go używać w projektach IT.
Dlaczego CSV wciąż dominuje w wymianie danych
CSV (Comma-Separated Values) to format, który przetrwał dekady nie dlatego, że jest doskonały, lecz dlatego, że jest prosty. Plik tekstowy, wiersze oddzielone znakiem nowej linii, kolumny — separatorem. Każdy system potrafi go wyeksportować, każdy arkusz kalkulacyjny — zaimportować. W projektach, które realizujemy w desi9n, CSV pojawia się regularnie: import danych klientów do CRM, eksport zamówień z e-commerce, zasilanie hurtowni danych BI, wymiana danych między systemami księgowymi. Problem w tym, że za pozorną prostotą kryje się szereg pułapek, które potrafią zepsuć dane w sposób trudny do wykrycia.
Separator — przecinek, średnik czy tabulator
Nazwa mówi „comma-separated", ale w Polsce i większości krajów europejskich domyślnym separatorem w Excelu jest średnik (;). Powód: przecinek pełni u nas funkcję separatora dziesiętnego (3,14 zamiast 3.14). Jeśli wyeksportujesz plik z przecinkiem jako separatorem i otworzysz go na polskim Windowsie w Excelu — wszystkie kolumny wpadną do jednej. LibreOffice Calc przynajmniej pyta o separator przy imporcie; Excel domyślnie bierze ustawienia regionalne systemu.
Inne spotykane separatory to tabulator (\t — pliki TSV), pipe (|) i znak ASCII 31 (unit separator, rzadko). Przy projektowaniu eksportu danych w systemach dedykowanych pozwalamy użytkownikowi wybrać separator i kodowanie — to kilka linii kodu, a oszczędza godziny wsparcia technicznego.
Kodowanie znaków — UTF-8, BOM i Windows-1250
Polskie znaki diakrytyczne (ą, ę, ó, ś, ź, ż, ć, ń, ł) to źródło połowy problemów z CSV. Plik zapisany w UTF-8 bez BOM (Byte Order Mark) otwarty w Excelu na Windowsie może wyświetlić „zażółć gęślą jaźń" jako „zażółć gÄ™ÅlÄ jaźń". Rozwiązanie: dodaj BOM (3 bajty: EF BB BF) na początku pliku. Excel rozpozna wtedy UTF-8 poprawnie. LibreOffice i Google Sheets radzą sobie bez BOM.
Starsze systemy polskie (szczególnie księgowe i ERP z lat 2000–2010) eksportują CSV w kodowaniu Windows-1250 (lub ISO 8859-2). Przy imporcie do nowoczesnego systemu potrzebujesz konwersji — w PHP: mb_convert_encoding($line, 'UTF-8', 'Windows-1250'), w Pythonie: open(path, encoding='cp1250'). Bez tego polskie znaki zamieniają się w pytajniki lub krzaki.
Typy danych — CSV nie wie, co przechowuje
Format CSV nie definiuje typów — wszystko jest ciągiem tekstowym. Interpretacja zależy od programu odczytującego, co prowadzi do zaskakujących efektów:
- Liczby z zerami wiodącymi — kod pocztowy
01-234lub numer konta0012345trafia do Excela jako liczba i traci zera. Obejście: otocz wartość cudzysłowem i poprzedź znakiem równości (="01-234") lub importuj kolumnę jako tekst. - Daty — zapis
03/04/2025może być interpretowany jako 3 kwietnia lub 4 marca, zależnie od ustawień regionalnych. Stosuj ISO 8601 (2025-04-03). - Separatory dziesiętne —
1,5w polskim pliku to półtora, ale system anglojęzyczny odczyta to jako dwie kolumny. - Wartości logiczne —
true/false,1/0,tak/nie— brak standardu, każdy system interpretuje po swojemu.
CSV injection — zagrożenie, o którym mało kto pamięta
Jeśli komórka CSV zaczyna się od znaku =, +, - lub @, Excel i LibreOffice potraktują ją jako formułę. Atakujący może wstawić do pola formularza wartość typu =CMD|'/C calc'!A0 — po otwarciu wyeksportowanego CSV w Excelu system wykona polecenie systemowe. To nie teoria: OWASP dokumentuje ten wektor ataku jako „CSV Injection" (inaczej „Formula Injection").
Obrona jest prosta: przy eksporcie danych do CSV poprzedź podejrzane wartości znakiem apostrofu (') lub tabulatorem. W PHP i Pythonie warto napisać funkcję sanityzującą, która sprawdza pierwszy znak każdej komórki. Przy imporcie CSV do bazy danych stosuj parametryzowane zapytania — nigdy nie wstawiaj wartości z CSV bezpośrednio do SQL.
Narzędzia do walidacji i naprawy CSV
Zanim zaimportujesz plik do bazy danych, warto go zwalidować. W Pythonie biblioteka csvkit oferuje narzędzie csvclean, które wykrywa niezamknięte cudzysłowy i niespójną liczbę kolumn. W PHP natywna funkcja fgetcsv() radzi sobie z prostymi przypadkami, ale przy dużych plikach (powyżej 100 MB) lepiej użyć SplFileObject w trybie strumieniowym — unikniesz załadowania całego pliku do pamięci. W systemach, które budujemy, import CSV zawsze przechodzi przez etap walidacji: sprawdzenie kodowania, liczby kolumn, typów danych i obecności wymaganych pól. Dopiero po walidacji dane trafiają do bazy.
Typowe błędy eksportu i importu
- Brak wiersza nagłówkowego lub niezgodność nagłówków z danymi (przesunięcie kolumn).
- Niezamknięte cudzysłowy — jedno pole z cytatem przesuwa wszystkie kolejne kolumny.
- Znaki nowej linii wewnątrz pola — jeśli pole nie jest otoczone cudzysłowami, parser potraktuje je jako nowy wiersz.
- Mieszane kodowanie w jednym pliku — zdarza się, gdy dane pochodzą z różnych źródeł i są łączone bez konwersji.
- Puste wiersze na końcu pliku — niektóre edytory dodają pusty wiersz, co parser interpretuje jako dodatkowy rekord z pustymi polami.
- Eksport z Excela przez „Zapisz jako CSV" bez sprawdzenia ustawień — Excel użyje separatora z ustawień regionalnych systemu, co może być niezgodne z oczekiwaniami systemu importującego.
Kiedy CSV to za mało
CSV sprawdza się przy płaskich, tabelarycznych danych — lista klientów, katalog produktów, eksport zamówień. Gdy dane mają strukturę zagnieżdżoną (np. zamówienie z wieloma pozycjami, każda z wariantami), CSV wymusza denormalizację i powtarzanie wierszy. W takich przypadkach lepszym wyborem jest JSON (czytelny, obsługiwany natywnie przez większość API), JSON Lines (jeden obiekt JSON per wiersz — łatwy do strumieniowania) albo Apache Parquet (kolumnowy format binarny, idealny do analityki na dużych zbiorach). Przy integracji systemów w czasie rzeczywistym zwykle lepiej postawić na REST/GraphQL API niż na wymianę plików.
Co z tym zrobić u siebie
- Ustal standard eksportu: UTF-8 z BOM, średnik jako separator (dla polskich użytkowników), ISO 8601 dla dat.
- Dodaj sanityzację CSV injection przy każdym eksporcie danych użytkownika.
- Przetestuj import na realnych danych — z polskimi znakami, zerami wiodącymi i datami w różnych formatach.
- Napisz walidator importu: sprawdzenie kodowania, liczby kolumn, typów — zanim dane trafią do bazy.
- Jeśli CSV nie wystarcza — rozważ JSON, Parquet lub bezpośrednie API. Przy budowie integracji między systemami dobieramy format do skali i wymagań.
Źródła
- RFC 4180 — Common Format and MIME Type for CSV Files — jedyny quasi-standard definiujący format CSV.
- OWASP — CSV Injection — opis wektora ataku i metod obrony.
- W3C — Tabular Data Model — rekomendacja opisująca metadane i typy w danych tabelarycznych.
- Kalendarze w aplikacjach — powiązany artykuł o pułapkach z datami (istotne przy eksporcie dat w CSV).