Dlaczego twój plik CSV otwiera się w Excelu niepoprawnie
Excel nie zagląda do środka pliku .csv, żeby ustalić, czym jest rozdzielony. Kiedy klikniesz taki plik dwa razy, program używa separatora listy z ustawień regionalnych systemu operacyjnego, a jeśli ten nie pasuje do pliku, każdy wiersz ląduje w jednej kolumnie.
W Wielkiej Brytanii i w Stanach Zjednoczonych separatorem listy jest przecinek. W większości Europy kontynentalnej i w dużej części Ameryki Łacińskiej, gdzie separatorem dziesiętnym jest przecinek, separatorem listy jest średnik. Dlatego poprawny plik rozdzielony przecinkami wysłany z Londynu otwiera się w Amsterdamie, Berlinie albo Madrycie jako jedna zgnieciona kolumna, a plik ze średnikami robi to samo w drugą stronę. Nic nie jest uszkodzone; plik jest po prostu czytany przy błędnym założeniu.
Trzy sposoby na poprawne otwarcie
- Zaimportuj zamiast otwierać. W Excelu wybierz Dane, potem Pobierz dane albo Z tekstu/CSV, i wskaż w oknie dialogowym separator oraz kodowanie. To jedyna droga, która daje pełną kontrolę, i jedyna, która przetrwa wysyłkę do innego kraju.
- Zmień rozszerzenie pliku na
.txt. Excel odmawia zgadywania dla pliku.txti zamiast tego pokazuje kreatora importu, który pyta o separator. - Przekonwertuj plik na separator, którego oczekuje odbiorca, zanim go wyślesz. Jeśli wiesz, że odbiorca pracuje w ustawieniach ze średnikiem, wyślij mu plik ze średnikami.
Wiersz sep=
Excel i LibreOffice rozumieją specjalny pierwszy wiersz, który nadpisuje ustawienie regionalne:
sep=;
name;email;country
Ada;ada@example.com;UK
To działa i jest częstym rozwiązaniem problemu "klient nie może otworzyć mojego eksportu". Pamiętaj jednak, że to konwencja arkuszy kalkulacyjnych, a nie część jakiejkolwiek specyfikacji CSV. Podaj ten sam plik skryptowi albo importowi do bazy danych, a sep=; zostanie odczytane jako zwykły pierwszy wiersz i zwykle stanie się fałszywym nagłówkiem kolumny. Używaj go do plików przeznaczonych dla człowieka z arkuszem kalkulacyjnym, a nie do wymiany między maszynami.
Cudzysłowy i znaki ucieczki
Nieformalnym punktem odniesienia dla CSV jest RFC 4180, które opisuje powszechną praktykę, zamiast ją narzucać. Jego reguły są krótkie:
- Pole musi być ujęte w cudzysłowy podwójne, jeśli zawiera separator, cudzysłów podwójny albo znak końca wiersza.
- Cudzysłów podwójny wewnątrz pola w cudzysłowach zapisuje się dwukrotnie.
id,name,note
1,"Smith, Ada","She said ""hello"" twice"
2,"Multi
line note",fine
Wiersz 1 ma trzy pola, a nie cztery, ponieważ przecinek siedzi wewnątrz cudzysłowów. Podwojone cudzysłowy zwijają się przy parsowaniu do pojedynczych. Wiersz 2 zawiera prawdziwy znak końca wiersza wewnątrz pola w cudzysłowach, co jest dozwolone i dlatego nie da się rzetelnie podzielić pliku CSV, licząc wiersze.
Poprzedzanie odwrotnym ukośnikiem (\, albo \") to inna konwencja, używana przez niektóre narzędzia bazodanowe. Nie jest to zgodne z RFC 4180 i ścisły parser tego nie zrozumie, więc jeśli twój plik używa odwrotnych ukośników, powiedz o tym wprost temu, co go czyta.
Najczęstszą usterką spotykaną w praktyce jest plik zapisany przez sklejanie ciągów znaków, w którym wartość zawierająca przecinek albo zabłąkany cudzysłów w ogóle nie została ujęta w cudzysłowy. Powstają z tego wiersze z większą liczbą pól niż nagłówek, czyli dokładnie to, na co wskaże walidator.
BOM i dlaczego znaki diakrytyczne zamieniają się w krzaki
Znacznik kolejności bajtów (BOM) to trzy niewidoczne bajty na samym początku pliku, które oznaczają go jako UTF-8. Excel w systemie Windows od dawna na nim polega: bez znacznika BOM potrafi wrócić do dawnego kodowania regionalnego, więc Müller wyświetla się jako Müller, a café jako café.
Kosztem jest to, że inne parsery nie zawsze go usuwają. Jeśli twój skrypt zgłasza pierwszą kolumnę o nazwie \ufeffid zamiast id albo wyszukiwanie po "id" w tajemniczy sposób zawodzi wyłącznie na pierwszej kolumnie, winowajcą jest BOM. Wczytaj plik z kodowaniem utf-8-sig, a problem zniknie.
Reguła praktyczna: dołączaj BOM do plików przeznaczonych do otwarcia w Excelu, a pomijaj go w plikach przeznaczonych do parsowania przez oprogramowanie.
Znaki końca wiersza
Narzędzia windowsowe zapisują na końcu każdego wiersza \r\n, narzędzia uniksowe \n. Większość parserów radzi sobie z jednym i drugim, ale naiwne dzielenie po \n zostawia powrót karetki przyklejony do ostatniego pola każdego wiersza i właśnie dlatego wartość, która wygląda jak UK, odmawia równości z "UK" w porównaniach.
Jak rozpoznać, jakiego separatora plik naprawdę używa
Otwórz plik w zwykłym edytorze tekstu, nigdy w arkuszu kalkulacyjnym, bo arkusz już podjął decyzję za ciebie. Następnie:
- Spójrz na pierwsze dwa albo trzy wiersze. Który znak kandydat (przecinek, średnik, tabulator, kreska pionowa) pojawia się pomiędzy tym, co ewidentnie jest wartościami pól?
- Policz każdego kandydata w kilku wierszach. Prawdziwy separator daje tę samą liczbę w każdym wierszu, o jeden mniejszą niż liczba kolumn. Znak, który w jednym wierszu pojawia się siedem razy, a w kolejnym dwa, jest danymi, a nie strukturą.
- Licz poza fragmentami ujętymi w cudzysłowy. W
"Smith, Ada"przecinek jest treścią, a naiwne liczenie go sprawia, że przecinki wyglądają na niekonsekwentne w pliku, który jest naprawdę rozdzielony przecinkami. - Uważaj na przecinki dziesiętne. Plik europejski często ma zarówno separatory w postaci średników, jak i przecinki wewnątrz liczb, jak w
Ada;1.234,56;UK. Jeśli widzisz przecinki wyłącznie pomiędzy cyframi, to separatory dziesiętne, a nie separatory pól. - Zanim cokolwiek stwierdzisz, sprawdź pierwsze bajty pod kątem wiersza
sep=albo znacznika BOM.
Na osobną wzmiankę zasługują tabulatory. Plik rozdzielony tabulatorami jest zwykle najbezpieczniejszą rzeczą, jaką można podać arkuszowi kalkulacyjnemu, ponieważ tabulatory prawie nigdy nie występują wewnątrz prawdziwych wartości, a Excel dobrze je obsługuje przy imporcie. Haczyk polega na tym, że tabulatory są niewidoczne, więc plik, w którym ktoś wyrównał kolumny spacjami, wygląda identycznie i parsuje się do niczego użytecznego.
Gdy już znasz separator, konwersja pliku jest czynnością mechaniczną: wczytaj go z właściwym ogranicznikiem i sposobem cytowania, a potem zapisz z tym, którego oczekuje miejsce docelowe, albo przekonwertuj go na JSON i całkowicie pomiń kwestię ogranicznika.