Eksport zamówień wygląda poprawnie, dopóki przecinek w nazwie produktu nie przesunie kwoty do sąsiedniej kolumny. Plik CSV to tekstowa tabela, którą łatwo przenieść między programami, ale sposób zapisu pól zależy od separatora, cudzysłowów i kodowania.

Poprawny import zaczyna się od rozpoznania reguł pliku
- Rozdzielaj rekordy zgodnie z formatem, nie przez proste cięcie tekstu.
- Ustaw separator i kulturę jawnie, bo domyślne wartości mogą zależeć od środowiska.
- Przetwarzaj wiersze strumieniowo, gdy plik jest duży.
- Sprawdź wynik przed użyciem, szczególnie typy danych i wartości pochodzące od użytkowników.
Przecinek i koniec wiersza nie zawsze kończą pole
Wartość „Kawa, mielona” może być jednym polem, choć zawiera przecinek. Podobnie tekst w cudzysłowie może mieć znak nowej linii, więc rozcięcie pliku po każdym przecinku albo końcu wiersza potrafi rozsypać całą tabelę.
RFC 4180 opisuje popularne reguły CSV: pola mogą być ujęte w podwójne cudzysłowy, a cudzysłów wewnątrz takiego pola zapisuje się podwójnie. Dokument dopuszcza też znaki nowej linii wewnątrz pola. Nie jest jednak uniwersalnym kontraktem dla wszystkich eksportów, dlatego najpierw sprawdź próbkę pliku i wymagania systemu, który go wygenerował.
Przykładowy wiersz 42;"Kawa, mielona";"Klient napisał: ""proszę o kontakt""" ma trzy pola, mimo że zawiera kilka separatorów i cudzysłowów. Metoda Split nie zna tych reguł; parser CSV je rozpoznaje.
CsvHelper mapuje rekordy, a TextFieldParser wystarcza do prostych plików
Wybór narzędzia zależy od tego, co ma się stać z danymi po imporcie. CsvHelper ułatwia mapowanie kolumn na obiekty C#, obsługę nagłówków i iteracyjne pobieranie rekordów. TextFieldParser z przestrzeni nazw Microsoft.VisualBasic.FileIO potrafi czytać pola rozdzielane oraz pliki o stałej szerokości i może wystarczyć w małym narzędziu bez rozbudowanego mapowania.
Przy typowanym imporcie wybrałbym CsvHelper: mapowanie na klasę czytelniej ujawnia, które kolumny program wykorzystuje. Gdy potrzebne jest jednorazowe odczytanie prostego pliku, TextFieldParser może ograniczyć liczbę zależności. W obu przypadkach jawnie ustaw separator, zamiast zakładać, że każdy plik używa przecinka.
W polskich eksportach spotyka się średnik, między innymi wtedy, gdy przecinek pełni rolę separatora dziesiętnego. Samo rozszerzenie .csv nie rozstrzyga, jakiego separatora użyto. Przy imporcie kwot uwzględnij też kulturę, według której parser zamienia tekst na typ liczbowy.
Duży plik czytaj rekord po rekordzie
Przy eksporcie zawierającym wiele zamówień kuszące jest wczytanie całej zawartości przez File.ReadAllText, a potem podzielenie jej na wiersze. Taki kod trzyma pełny tekst w pamięci i nadal nie radzi sobie z nową linią wewnątrz cytowanego pola. Parser strumieniowy oddaje rekordy pojedynczo, więc program może przetwarzać dane bez budowania kopii całego pliku.
Poniższy przykład zakłada nagłówki Id, Produkt i Cena, średnik jako separator oraz polską kulturę dla konwersji wartości. W produkcji trzeba dopasować te ustawienia do umowy z dostawcą pliku.
using System.Globalization;
using System.Text;
using CsvHelper;
using CsvHelper.Configuration;
public sealed class ImportRow
{
public string Id { get; set; } = "";
public string Produkt { get; set; } = "";
public decimal Cena { get; set; }
}
var config = new CsvConfiguration(CultureInfo.GetCultureInfo("pl-PL"))
{
Delimiter = ";",
HasHeaderRecord = true
};
using var reader = new StreamReader(
"zamowienia.csv",
new UTF8Encoding(false),
detectEncodingFromByteOrderMarks: true);
using var csv = new CsvReader(reader, config);
foreach (var row in csv.GetRecords())
{
Console.WriteLine($"{row.Id}: {row.Produkt}, {row.Cena}");
}
Identyfikator jest tekstem celowo: numer typu 00042 po konwersji na liczbę straciłby zera wiodące. Jeśli chcesz zachować tylko część rekordów, od razu filtruj je w pętli. Nie zamieniaj wyniku na listę bez potrzeby, bo wtedy ponownie zgromadzisz wszystkie obiekty w pamięci.
Objaw importu podpowiada, gdzie szukać błędu
Niepoprawny wynik często wygląda jak błąd w danych, choć przyczyna leży w ustawieniach importu. Zanim zaczniesz zmieniać plik, porównaj objaw z konfiguracją parsera.
| Sytuacja lub objaw | Prawdopodobna przyczyna | Co zrobić |
|---|---|---|
| Cały wiersz trafia do jednej kolumny | Ustawiony separator nie pasuje do pliku | Sprawdź kilka wierszy i ustaw jawnie przecinek, średnik albo tabulator |
| Polskie znaki są nieczytelne | Parser używa innego kodowania niż plik | Ustal kodowanie źródła; dla UTF-8 sprawdź też, czy plik zawiera BOM |
| Opis produktu pojawia się w kilku wierszach | Parser potraktował nową linię wewnątrz cytowanego pola jak koniec rekordu | Użyj parsera CSV, który obsługuje cytowane pola wielowierszowe |
| Kwota z przecinkiem nie daje się zamienić na liczbę | Kultura konwersji nie pasuje do zapisu liczby | Ustaw kulturę zgodną ze źródłem albo wczytaj wartość jako tekst i zweryfikuj ją przed konwersją |
| Identyfikator stracił zera na początku | Parser zamienił go na typ liczbowy | Przechowuj identyfikator jako tekst |
Najpierw porównaj separator, kodowanie i ustawienia kultury z pierwszymi kilkoma rekordami. Nie naprawiaj przecinków w danych ręcznie, dopóki nie sprawdzisz, czy cytowanie pól jest zgodne z regułami eksportu.
Eksport CSV może uruchomić formułę w arkuszu
Gdy tworzysz plik do pobrania, zagrożeniem może być wartość pochodząca od użytkownika, która zaczyna się od znaku interpretowanego przez arkusz jako formuła. Po otwarciu takiego eksportu w programie arkuszowym zawartość komórki może zostać wykonana, a samo ujęcie tekstu w cudzysłowy CSV nie daje pewnej ochrony.
OWASP opisuje ten problem jako CSV Injection. Zabezpieczenie zależy od tego, kto otworzy plik: metoda przeznaczona do wyświetlania danych w Excelu może zmienić wartość, którą później odczyta inny system. Rozdziel więc eksport dla człowieka od pliku wymiany maszynowej, a dla obu określ i przetestuj zasady oczyszczania danych.
Przy imporcie z niezaufanego źródła traktuj komórki jako dane, nie jako polecenia. Przy eksporcie nie obiecuj uniwersalnego zabezpieczenia bez sprawdzenia zachowania docelowego programu, bo arkusze mogą różnie interpretować wartości.
Ustal kontrakt pliku, zanim podłączysz import
Najmniej kłopotów daje krótki kontrakt: separator, kodowanie, obecność nagłówka, format liczb oraz reguły cytowania. Wtedy można ustawić parser raz i odrzucić plik, który nie spełnia oczekiwań, zamiast po cichu importować przesunięte kolumny.
Moim zdaniem to lepsza granica odpowiedzialności niż „sprytne” zgadywanie formatu przy każdym uruchomieniu. Jeśli kontrakt jest jawny, plik CSV pozostaje prostym tekstem do wymiany danych, a błędy wychodzą przy wejściu, zanim trafią do dalszej części aplikacji.
