Muszę załadować i użyć danych pliku CSV w C ++. W tym momencie może to być po prostu parser rozdzielany przecinkami (tzn. Nie martw się o ucieczkę nowych wierszy i przecinków). Główną potrzebą jest parser wiersz po wierszu, który zwróci wektor dla następnej linii za każdym razem, gdy wywoływana jest metoda.
Znalazłem ten artykuł, który wygląda dość obiecująco: http://www.boost.org/doc/libs/1_35_0/libs/spirit/example/fundamental/list_parser.cpp
Nigdy nie korzystałem z Ducha Wzmocnienia, ale chętnie go wypróbuję. Ale przeoczę tylko wtedy, gdy nie ma prostszego rozwiązania.
boost::spirit
analizy. Chodzi bardziej o parsowanie gramatyki, dzięki parsowaniu prostego formatu pliku. Ktoś z mojego zespołu próbował użyć go do analizy XML i debugowanie było uciążliwe. Unikaj,boost::spirit
jeśli to możliwe.spirit
jest dość trudny w użyciu dla biblioteki kombinacji parserów. Mając (bardzo przyjemne) doświadczenie z(atto)parsec
bibliotekami Haskells spodziewałem się, że (duch) będzie działał podobnie, ale zrezygnowałem z niego po walce z błędami kompilatora linii 600.Odpowiedzi:
Jeśli nie przejmujesz się przecinaniem przecinków i znaków nowej linii,
ORAZ nie możesz osadzać przecinków i znaków nowej linii w cudzysłowach (jeśli nie możesz uciec, to ...),
to tylko trzy wiersze kodu (OK 14 -> Ale to jest tylko 15, aby odczytać cały plik).
Chciałbym po prostu stworzyć klasę reprezentującą wiersz.
Następnie przesyłaj strumieniowo do tego obiektu:
Ale przy odrobinie pracy technicznie możemy stworzyć iterator:
źródło
istream::operator>>
(jak Eigen), dodajinline
przed deklaracją operatora, aby to naprawić.Rozwiązanie wykorzystujące Boost Tokenizer:
źródło
Moja wersja nie używa niczego poza standardową biblioteką C ++ 11. Dobrze radzi sobie z cytatem Excel CSV:
Kod jest zapisywany jako maszyna o stanie skończonym i zużywa jeden znak na raz. Myślę, że łatwiej jest o tym myśleć.
źródło
const char *vinit[] = {""}; vector<string> fields(vinit, end(vinit));
Biblioteka C ++ String Toolkit Library (StrTk) ma klasę siatki tokenów, która pozwala ładować dane z plików tekstowych, łańcuchów znaków lub buforów znaków , a także analizować / przetwarzać je w sposób wiersz-kolumna.
Możesz określić ograniczniki wierszy i ograniczniki kolumn lub po prostu użyć wartości domyślnych.
Więcej przykładów można znaleźć tutaj
źródło
options.trim_dquotes = true
), nie obsługuje usuwania podwójnych cudzysłowów (np. Pola"She said ""oh no"", and left."
jako ciągu c"She said \"oh no\", and left."
). Musisz to zrobić sam.strtk
musisz także ręcznie obsługiwać pola cudzysłowów zawierające znaki nowego wiersza.Możesz użyć Boost Tokenizer z escaped_list_separator.
To używa tylko plików nagłówkowych tokenizera Boost, bez konieczności łączenia z bibliotekami boost.
Oto przykład (zobacz: Analizowanie pliku CSV za pomocą Boost Tokenizer w C ++, aby uzyskać szczegółowe informacje lub
Boost::tokenizer
):źródło
Używanie Ducha do analizowania plików CSV nie jest przesadą. Spirit doskonale nadaje się do zadań mikroparsowania. Na przykład w Spirit 2.1 jest to tak proste, jak:
Wektor v wypełnia się wartościami. Jest seria samouczków poruszających tę kwestię w nowych dokumentach Spirit 2.1, które właśnie zostały wydane wraz z Boost 1.41.
Samouczek przechodzi od prostego do złożonego. Parsery CSV są przedstawione gdzieś pośrodku i dotyczą różnych technik używania Ducha. Wygenerowany kod jest tak ścisły jak kod pisany ręcznie. Sprawdź wygenerowany asembler!
źródło
Jeśli NIE dba o parsowania CSV poprawnie, to to zrobi ... stosunkowo powoli, jak to działa jeden char naraz.
źródło
Korzystając z Boost Tokenizer escaped_list_separator dla plików CSV, należy pamiętać o następujących kwestiach:
Format CSV określony przez wiki stwierdza, że pola danych mogą zawierać separatory w cudzysłowach (obsługiwane):
Format CSV określony przez wiki stanowi, że pojedyncze cudzysłowy powinny być obsługiwane z podwójnymi cudzysłowami (escaped_list_separator usunie wszystkie znaki cudzysłowu):
Format CSV nie określa, że wszelkie znaki ukośnika powinny być usunięte (escaped_list_separator usunie wszystkie znaki specjalne).
Możliwe obejście problemu, aby naprawić domyślne zachowanie boost escaped_list_separator:
To obejście ma efekt uboczny polegający na tym, że puste pola danych reprezentowane przez podwójny cudzysłów zostaną przekształcone w token pojedynczego cudzysłowu. Podczas iteracji po tokenach należy sprawdzić, czy token jest pojedynczym cudzysłowiem i traktować go jak pusty ciąg.
Nie ładnie, ale działa, o ile w cytatach nie ma nowych linii.
źródło
Możesz zajrzeć do mojego projektu FOSS CSVfix ( zaktualizowany link ), który jest edytorem strumienia CSV napisanym w C ++. Parser CSV nie jest nagrodą, ale spełnia swoje zadanie, a cała paczka może zrobić to, czego potrzebujesz, bez pisania kodu.
Zobacz alib / src / a_csv.cpp dla parsera CSV, a csvlib / src / csved_ioman.cpp (
IOManager::ReadCSV
) dla przykładu użycia.źródło
Ponieważ wszystkie pytania CSV wydają się być tutaj przekierowywane, pomyślałem, że opublikuję tutaj swoją odpowiedź. Ta odpowiedź nie dotyczy bezpośrednio pytania zadającego. Chciałem móc czytać w strumieniu, o którym wiadomo, że jest w formacie CSV, a także typy każdego pola były już znane. Oczywiście poniższa metoda może być użyta do traktowania każdego pola jako typu łańcucha.
Jako przykład tego, jak chciałem móc używać strumienia wejściowego CSV, rozważ następujące dane wejściowe (wzięte ze strony wikipedii na CSV ):
Potem chciałem móc odczytać takie dane:
Takie było rozwiązanie.
Dzięki następującym pomocnikom, które można uprościć dzięki nowym zintegrowanym szablonom cech w C ++ 11:
Wypróbuj online!
źródło
Napisałem tylko parser C ++ 11 CSV zawierający tylko nagłówki . Jest dobrze przetestowany, szybki, obsługuje całą specyfikację CSV (pola cytowane, separator / terminator w cudzysłowach, zmiany znaczenia cytatów itp.) I można go konfigurować w celu uwzględnienia CSV, które nie są zgodne ze specyfikacją.
Konfiguracja odbywa się za pomocą płynnego interfejsu:
Parsowanie to tylko zakres oparty na pętli:
źródło
Inną bibliotekę we / wy CSV można znaleźć tutaj:
http://code.google.com/p/fast-cpp-csv-parser/
źródło
Inne rozwiązanie podobne do odpowiedzi Lokiego Astari w C ++ 11. Rzędy tutaj są
std::tuple
danego typu. Kod skanuje jedną linię, a następnie skanuje do każdego separatora, a następnie konwertuje i zrzuca wartość bezpośrednio do krotki (z odrobiną kodu szablonu).Zaliczki:
std::tuple<t1, ...>
viaoperator>>
.Czego brakuje:
Główny kod:
Podałem mały działający przykład na GitHub ; Używałem go do analizowania danych liczbowych i spełniło swoje zadanie.
źródło
Oto kolejna implementacja analizatora składni Unicode CSV (współpracuje z wchar_t). Napisałem część, a resztę napisał Jonathan Leffler.
Uwaga: ten analizator składni ma na celu jak najwierniejszą replikację zachowania programu Excel, szczególnie podczas importowania uszkodzonych lub źle sformatowanych plików CSV.
To jest oryginalne pytanie - parsowanie pliku CSV z polami wielowierszowymi i unikaniem podwójnych cudzysłowów
To jest kod jako SSCCE (krótki, samodzielny, poprawny przykład).
źródło
Potrzebowałem łatwej w użyciu biblioteki C ++ do parsowania plików CSV, ale nie mogłem znaleźć żadnej dostępnej, więc skończyłem z budowaniem jednego. Rapidcsv to biblioteka zawierająca tylko nagłówki C ++ 11, która zapewnia bezpośredni dostęp do analizowanych kolumn (lub wierszy) jako wektorów w wybranym typie danych. Na przykład:
źródło
Przepraszam, ale to wszystko wydaje się być bardzo skomplikowaną składnią, aby ukryć kilka linii kodu.
Dlaczego nie to:
źródło
",\n"
w sznurku?Oto kod do odczytu matrycy, pamiętaj, że masz również funkcję csvwrite w Matlabie
źródło
Możesz otworzyć i odczytać plik .csv za pomocą funkcji fopen, fscanf, ale ważne jest, aby parsować dane. Najprostszym sposobem na parsowanie danych za pomocą separatora. W przypadku .csv separatorem jest „,”.
Załóżmy, że plik data1.csv wygląda następująco:
możesz tokenizować dane i przechowywać je w tablicy char, a następnie użyć funkcji atoi () itp. do odpowiednich konwersji
[^,], ^ -it odwraca logikę, oznacza dopasowanie dowolnego łańcucha, który nie zawiera przecinka, a następnie ostatniego, mówi o dopasowaniu przecinka, który zakończył poprzedni ciąg.
źródło
Pierwszą rzeczą, którą musisz zrobić, to upewnić się, że plik istnieje. Aby to osiągnąć, wystarczy spróbować otworzyć strumień plików na ścieżce. Po otwarciu strumienia plików użyj stream.fail (), aby sprawdzić, czy działa zgodnie z oczekiwaniami, czy nie.
Musisz również sprawdzić, czy podany plik jest poprawnego typu. Aby to osiągnąć, musisz przejrzeć podaną ścieżkę pliku, aż znajdziesz rozszerzenie pliku. Po rozszerzeniu upewnij się, że jest to plik .csv.
Ta funkcja zwróci rozszerzenie pliku, które zostanie użyte później w komunikacie o błędzie.
Ta funkcja wywoła kontrole błędów utworzone powyżej, a następnie przeanalizuje plik.
źródło
Musisz być dumny, kiedy używasz czegoś tak pięknego jak
boost::spirit
Tutaj moja próba parsera (prawie) zgodnego ze specyfikacjami CSV w tym specyfikacji CSV linku (nie potrzebowałem łamania linii w polach. Również spacje wokół przecinków są odrzucane).
Po przezwyciężeniu szokującego doświadczenia oczekiwania na 10 sekund na skompilowanie tego kodu :) możesz usiąść i cieszyć się.
Skompilować:
Test (przykład skradziony z Wikipedii ):
źródło
To rozwiązanie wykrywa te 4 przypadki
cała klasa jest na
https://github.com/pedro-vicente/csv-parser
Czyta plik znak po znaku i odczytuje 1 wiersz na raz do wektora (ciągów znaków), dlatego nadaje się do bardzo dużych plików.
Wykorzystanie jest
Iteruj, aż zostanie zwrócony pusty wiersz (koniec pliku). Wiersz to wektor, w którym każdy wpis jest kolumną CSV.
deklaracja klasy
implementacja
źródło
Możesz także przyjrzeć się możliwościom
Qt
biblioteki.Obsługuje wyrażenia regularne, a klasa QString ma ładne metody, np.
split()
Zwraca QStringList, listę ciągów uzyskanych przez podzielenie oryginalnego ciągu za pomocą ogranicznika. Powinien wystarczyć do pliku csv ..Aby uzyskać kolumnę o podanej nazwie nagłówka, używam: c ++ dziedziczenie Qt problem qstring
źródło
Jeśli nie chcesz poradzić sobie z włączeniem ulepszenia do swojego projektu (jest on znacznie duży, jeśli zamierzasz go używać tylko do analizy CSV ...)
Miałem szczęście z analizowaniem CSV tutaj:
http://www.zedwood.com/article/112/cpp-csv-parser
Obsługuje cytowane pola - ale nie obsługuje znaków \ n wbudowanych (co prawdopodobnie jest dobre dla większości zastosowań).
źródło
To jest stary wątek, ale wciąż znajduje się na górze wyników wyszukiwania, więc dodałem moje rozwiązanie za pomocą std :: stringstream i prostej metody zastępowania łańcucha przez Yves Baumes, którą tu znalazłem.
Poniższy przykład przeczyta plik linia po linii, zignoruje linie komentarza zaczynające się od // i parsuje pozostałe linie w kombinację ciągów, liczb całkowitych i podwójnych. Stringstream wykonuje parsowanie, ale oczekuje, że pola będą oddzielone spacjami, więc używam stringreplace, aby najpierw zamienić przecinki w spacje. Obsługuje tabulatory w porządku, ale nie radzi sobie z ciągami cytowanymi.
Złe lub brakujące dane wejściowe są po prostu ignorowane, co może, ale nie musi być dobre, w zależności od okoliczności.
źródło
Za to, co jest warte, oto moja implementacja. Zajmuje się wprowadzaniem ciągu, ale można go łatwo dostosować do łańcucha. Nie obsługuje znaku nowej linii w polach (podobnie jak moja aplikacja, ale dodanie jego obsługi nie jest zbyt trudne) i nie jest zgodny z końcem wiersza „\ r \ n” zgodnie z RFC (zakładając, że używasz std :: getline), ale poprawnie obsługuje przycinanie białych znaków i cudzysłowy (miejmy nadzieję).
źródło
Oto gotowa do użycia funkcja, jeśli wszystko, czego potrzebujesz, to załadować plik danych podwójnych (bez liczb całkowitych, bez tekstu).
źródło
Innym szybkim i łatwym sposobem jest użycie
Boost.Fusion I/O
:Wyjścia:
źródło
Napisałem dobry sposób na parsowanie plików CSV i pomyślałem, że powinienem dodać go jako odpowiedź:
źródło
Możliwe jest użycie
std::regex
.W zależności od rozmiaru pliku i dostępnej pamięci można go odczytać wiersz po wierszu lub całkowicie w pliku
std::string
.Do odczytania pliku można użyć:
wtedy możesz dopasować do tego, co faktycznie można dostosować do swoich potrzeb.
źródło
Ponieważ nie jestem przyzwyczajony do zwiększania teraz, zasugeruję prostsze rozwiązanie. Załóżmy, że plik .csv ma 100 linii z 10 liczbami w każdej linii oddzielonymi znakiem „,”. Możesz załadować te dane w postaci tablicy z następującym kodem:
źródło