2012-05-30 15 views
8

Estoy tratando de analizar un archivo CSV simple, con los datos en un formato como:rápido, simple CSV de análisis en 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 

Por lo tanto, un archivo de formato muy simple y fijo. Estoy almacenando cada columna de estos datos en un vector STL. Como tal He tratado de mantenerse la forma en C++ usando la librería estándar, y mi aplicación dentro de un bucle se ve algo como:

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); 

El problema es que esto es extremadamente lento (hay más de 1 millón de filas por los datos archivo), y me parece un poco poco elegante. ¿Hay un enfoque más rápido usando la biblioteca estándar, o debería simplemente usar las funciones de stdio? Me parece que todo este bloque de código se reduciría a una única llamada fscanf.

¡Gracias de antemano!

+1

duplicado de la siguiente pregunta: http://stackoverflow.com/questions/1120140/csv-parser-in-c –

+0

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

Respuesta

8

El uso de 7 secuencias de cadenas cuando puede hacerlo con una sola no ayuda a wrt. actuación. Tal vez puedas probar:

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); 

Si conoce el número de líneas en el archivo, se puede cambiar el tamaño de los vectores antes de la lectura y luego usar operator[] en lugar de at(). De esta forma evitará la comprobación de límites y así obtendrá un poco de rendimiento.

+0

¡Perfecto! Funciona mucho, mucho mejor. ¡Gracias por la pista sobre los caracteres para comer las comas! –

+0

@KyleLynch: seriamente le aconsejo que verifique que el 'char' se haya inicializado en comas. Además, debe comprobar que la transmisión sea válida O establecer los indicadores de excepciones, para advertir en caso de salida incorrecta. –

+0

pequeña cosa: un char para comer las comas sería suficiente – IceFire

2

Creo que el principal cuello de botella (aparte de la E/S no búfer basada en getline()) es el análisis sintáctico de cadenas. Como tiene el símbolo "," como delimitador, puede realizar un escaneo lineal sobre la cadena y reemplazar todo "," por "\ 0" (el marcador de fin de cadena, cero-terminador).

Algo como esto:

// 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

No sé si esto va a ser más rápido que la respuesta aceptada, pero que también podría publicar de todos modos, en caso de que desee probarlo. Puede cargar todo el contenido del archivo con una sola llamada de lectura conociendo el tamaño del archivo utilizando fseek magic. Esto será mucho más rápido que varias llamadas de lectura.

entonces se podría hacer algo como esto para analizar la cadena:

//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], ",");