Einlesen einer CSV-Datei mit double-Werten
-
Hey Community,
ich suche eine sehr effiziente und schnelle Methode, eine große CSV-Datei in ein einfaches double-Array einzulesen. Die Datei besteht dabei aus x (x > 10.000) Reihen mit jeweils 1000 Spalten, und durch ein '\t' getrennt. So sieht meine bisherige Methode aus:int main() { ifstream file; double *numbers = new double[10000*1000]; // Hier ist Reihenzahl bekannnt int counter = -1; file.open("../file.txt"); while(!file.eof()) { file >> numbers[++counter]; } file.close(); }Selbst mit SSD ist diese Methode sehr langsam, und erfordert eine halbe Minute Wartezeit. Natürlich gäbe es die Möglichkeit, die Datei als Binärdatei zu speichern, und innerhalb eines Bruchteils einer Sekunde einzulesen, ist jedoch in diesem Fall nicht machbar. Wie würdet ihr da vorgehen?
dommynik
-
Hallo,
versuche mal ungepuffert einzulesen:
ifstream file("../file.txt"); file.setbuf(0, 0); while(file >> numbers[++counter]) { }(und die Leseschleife habe ich auch gleich mal richtig angepasst

-
Laut http://www.cplusplus.com/reference/fstream/ifstream/ ist diese Methode nicht implementiert? Ich probiere es mal mit http://www.cplusplus.com/reference/fstream/filebuf/
-
1. Lade den Dateiinhalt in den Speicher
2. Nutze std::stod oder eigene Funktion statt der stream operatoren.
3. Möglicherweise mit Memory mapped files. Das ist dann aber nicht standardkonform und ich habe damit auch kaum Erfahrung.Th69 schrieb:
versuche mal ungepuffert einzulesen:
Die Festplatte ist fast immer das langsamste und buffern reduziert die Anzahl an nötigen Lesezugriffen.
-
Eine kurze und elegante Möglichkeit wäre
std::vector<double> vec; vec.reserve(10000 * 1000); vec.insert( vec.begin(), std::istream_iterator<double>{file}, std::istream_iterator<double>{} );Vielleicht läuft das hier schneller:
#include <vector> #include <cstdio> #include <cstdlib> #include <iostream> int main() { std::vector<double> vec; vec.reserve( 10000 * 1000 ); auto file = std::fopen("test.cvs", "r"); for( unsigned x = 0; x != 2; ++x ) // Zeilenanzahl { for( unsigned y = 0; y != 5; ++y ) // Spaltenzahl { char str[15]; char *end_ptr = str; for(;;) { char next = fgetc(file); if( next == '\t' || next == '\n' ) break; *end_ptr++ = next; } *end_ptr = '\0'; vec.push_back( std::strtod(str, &end_ptr) ); } } std::fclose(file); for( auto i : vec ) std::cout << i << ' '; }Wobei die Datei so aussieht:
1 2 3 4 5 5 4 3 2 1Das ganze geht vielleicht noch viel effizienter, wenn die genaue Notation der Fließkommazahlen bekannt ist.
-
Versuch es mal mit der kompletten Datei einlesen:
std::vector<double> numbers; ifstream file; char delimiter = ";"; char* actualPos = readWholeFile(file); // gibt einen Pointer zurück der auf den Anfang der komplett eingelesenen CSV-Datei zeigt while (*actualPos != '\0') { delWhiteSpaces(); // löscht alle \t und Leerzeichen char* oldPos = actualPos; while (*actualPos != delimiter) { actualPos++; } char temp = *actualPos; *actualPos = '\0'; numbers.push_back(atof(oldPos)); *actualPos = temp actualPos++; // del ; }Die möglichen Fehler musst du halt selbst beachten.
-
Ok, hier der richtige Code für ungepufferten Zugriff:
file.rdbuf()->pubsetbuf(0, 0);Mich würde einfach mal interessieren, wie da so die Zeiten sind. Früher hat das bei den Festplatten immer einen enormen Geschwindigkeitsboost gegeben (da nicht immer doppelt die Daten kopiert werden mussten).
PS. @Marthog, hast du das selber mal wirklich ausprobiert?
-
Marthog schrieb:
2. Nutze std::stod oder eigene Funktion statt der stream operatoren.
Warum?