2012-05-30 8 views
8

Próbuję analizować prosty plik CSV z danymi w formacie takim jak:szybki, prosty CSV podczas analizowania w C++

20.5,20.5,20.5,0.794145,4.05286,0.792519,1 
20.5,30.5,20.5,0.753669,3.91888,0.749897,1 
20.5,40.5,20.5,0.701055,3.80348,0.695326,1 

tak, to bardzo prosty i ustalonym formacie pliku. Przechowuję każdą kolumnę tych danych w wektorze STL. Jako takie Próbowałem zawiesić C drogę ++ z wykorzystaniem biblioteki standardowej, a moja realizacja wewnątrz pętli wygląda mniej więcej tak:

string field; 
getline(file,line); 
stringstream ssline(line); 

getline(ssline, field, ','); 
stringstream fs1(field); 
fs1 >> cent_x.at(n); 

getline(ssline, field, ','); 
stringstream fs2(field); 
fs2 >> cent_y.at(n); 

getline(ssline, field, ','); 
stringstream fs3(field); 
fs3 >> cent_z.at(n); 

getline(ssline, field, ','); 
stringstream fs4(field); 
fs4 >> u.at(n); 

getline(ssline, field, ','); 
stringstream fs5(field); 
fs5 >> v.at(n); 

getline(ssline, field, ','); 
stringstream fs6(field); 
fs6 >> w.at(n); 

Problemem jest to, to jest bardzo powolny (istnieje ponad 1 milion wierszy na danych plik) i wydaje mi się nieco nieelegancki. Czy istnieje szybsze podejście przy użyciu standardowej biblioteki, czy powinienem po prostu użyć funkcji stdio? Wydaje mi się, że cały blok kodu zmniejszyłby się do pojedynczego wywołania fscanf.

Z góry dziękuję!

+1

duplikat następującym pytaniem: http://stackoverflow.com/questions/1120140/csv-parser-in-c –

+0

C CSV Parser: http://sourceforge.net/projects/cccsvparser C CSV Writer: http://sourceforge.net/projects/cccsvwriter – SomethingSomething

Odpowiedz

8

Używanie 7 strumieni strunowych, gdy można to zrobić z jednym tylko pewnym, nie pomaga. wydajność. Spróbuj to zamiast:

string line; 
getline(file, line); 

istringstream ss(line); // note we use istringstream, we don't need the o part of stringstream 

char c1, c2, c3, c4, c5; // to eat the commas 

ss >> cent_x.at(n) >> c1 >> 
     cent_y.at(n) >> c2 >> 
     cent_z.at(n) >> c3 >> 
     u.at(n) >> c4 >> 
     v.at(n) >> c5 >> 
     w.at(n); 

Jeśli znasz numer linii w pliku, można zmienić rozmiar wektorów przed czytaniem, a następnie użyć operator[] zamiast at(). W ten sposób unikasz sprawdzania granic i uzyskujesz w ten sposób niewielką wydajność.

+0

Idealny! Działa znacznie, znacznie lepiej. Dzięki za podpowiedź na temat znaków do jedzenia przecinków! –

+0

@KyleLynch: Poważnie zaleciłbym sprawdzenie, czy 'char' zostało zainicjowane na przecinki. Powinieneś także sprawdzić, czy strumień jest prawidłowy LUB ustawić flagi wyjątków, aby zostać ostrzeżonym w przypadku złego wyjścia. –

+0

drobiazg: wystarczy jeden znak, by zjeść przecinki. – IceFire

2

Wierzę, że główne wąskie gardło (odłożone na zasadzie braku bufora wejścia/wyjścia oparte na getline()) jest parsowanie ciągów. Ponieważ masz symbol "," jako separator, możesz wykonać liniowy skan przez ciąg i zastąpić wszystkie "," przez "\ 0" (znacznik końca łańcucha, terminator zerowy).

coś takiego:

// tmp array for the line part values 
double parts[MAX_PARTS]; 

while(getline(file, line)) 
{ 
    size_t len = line.length(); 
    size_t j; 

    if(line.empty()) { continue; } 

    const char* last_start = &line[0]; 
    int num_parts = 0; 

    while(j < len) 
    { 
     if(line[j] == ',') 
     { 
      line[j] = '\0'; 

      if(num_parts == MAX_PARTS) { break; } 

      parts[num_parts] = atof(last_start); 
      j++; 
      num_parts++; 
      last_start = &line[j]; 
     } 
     j++; 
    } 

    /// do whatever you need with the parts[] array 
} 
1

ja nie wiem, czy to będzie szybciej niż przyjętym odpowiedź, ale równie dobrze mogę pisać to i tak w przypadku, gdy chcesz spróbować. Możesz załadować całą zawartość pliku, wykonując jedno połączenie do odczytu, znając rozmiar pliku, używając jakiegoś fseek magic.. Będzie to znacznie szybsze niż wielokrotne czytanie.

Następnie można zrobić coś takiego przeanalizować swój ciąg:

//Delimited string to vector 
vector<string> dstov(string& str, string delimiter) 
{ 
    //Vector to populate 
    vector<string> ret; 
    //Current position in str 
    size_t pos = 0; 
    //While the the string from point pos contains the delimiter 
    while(str.substr(pos).find(delimiter) != string::npos) 
    { 
    //Insert the substring from pos to the start of the found delimiter to the vector 
    ret.push_back(str.substr(pos, str.substr(pos).find(delimiter))); 
    //Move the pos past this found section and the found delimiter so the search can continue 
    pos += str.substr(pos).find(delimiter) + delimiter.size(); 
    } 
    //Push back the final element in str when str contains no more delimiters 
    ret.push_back(str.substr(pos)); 
    return ret; 
} 

string rawfiledata; 

//This call will parse the raw data into a vector containing lines of 
//20.5,30.5,20.5,0.753669,3.91888,0.749897,1 by treating the newline 
//as the delimiter 
vector<string> lines = dstov(rawfiledata, "\n"); 

//You can then iterate over the lines and parse them into variables and do whatever you need with them. 
for(size_t itr = 0; itr < lines.size(); ++itr) 
    vector<string> line_variables = dstov(lines[itr], ","); 
Powiązane problemy