2012-05-30 23 views
8

मैं जैसे एक प्रारूप में डेटा के साथ एक सरल CSV फ़ाइल पार्स करने के लिए, कोशिश कर रहा हूँ:फास्ट, सी में सरल सीएसवी पार्सिंग ++

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 

तो, एक बहुत ही सरल और निश्चित प्रारूप फ़ाइल। मैं इस डेटा के प्रत्येक कॉलम को एसटीएल वेक्टर में संग्रहीत कर रहा हूं। इस तरह के रूप में मैं सी ++ रास्ता मानक पुस्तकालय का उपयोग कर रहने के लिए कोशिश की है, और एक पाश के भीतर मेरी कार्यान्वयन तरह दिखता है:

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

समस्या है, यह बेहद धीमी गति से है (वहाँ डेटा प्रति 1 लाख से अधिक पंक्तियां हों फ़ाइल), और मुझे थोड़ा सा सुरुचिपूर्ण लगता है। मानक पुस्तकालय का उपयोग कर एक तेज दृष्टिकोण है, या मैं सिर्फ stdio कार्यों का उपयोग करना चाहिए? ऐसा लगता है कि यह पूरा कोड ब्लॉक एक fscanf कॉल को कम कर देगा।

अग्रिम धन्यवाद!

+1

निम्नलिखित प्रश्न का डुप्लिकेट::

तब आप अपने स्ट्रिंग पार्स करने में कुछ इस तरह कर सकता है http://stackoverflow.com/questions/1120140/csv-parser-in-c –

+0

सी सीएसवी पार्सर: http://sourceforge.net/projects/cccsvparser सी सीएसवी लेखक: http://sourceforge.net/projects/cccsvwriter – SomethingSomething

उत्तर

8

7 स्ट्रिंग स्ट्रीम का उपयोग करके जब आप इसे केवल एक निश्चित के साथ कर सकते हैं तो wrt की मदद नहीं करता है। प्रदर्शन। बजाय इस प्रयास करें:

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

आप फ़ाइल में लाइनों की संख्या पता है, आप वैक्टर पहले पढ़ने के लिए आकार बदल सकते हैं और उसके बाद at() की operator[] का उपयोग करने के बजाय। इस तरह आप सीमाओं की जांच से बचते हैं और इस तरह थोड़ा प्रदर्शन प्राप्त करते हैं।

+0

बिल्कुल सही! यह बहुत बेहतर काम करता है। अल्पविराम खाने के लिए वर्णों के संकेत के लिए धन्यवाद! –

+0

@ केलीलिंच: मैं गंभीरता से सलाह दूंगा कि आप 'char' को अल्पविरामों में प्रारंभ किए गए थे। साथ ही, आपको यह जांचना चाहिए कि स्ट्रीम वैध है या खराब आउटपुट के मामले में चेतावनी देने के लिए अपवाद झंडे सेट करें। –

+0

छोटी चीज़: अल्पविराम खाने के लिए एक चार पर्याप्त होगा – IceFire

2

मुझे विश्वास है कि प्रमुख बाधा (गेटलाइन() - आधारित गैर-बफर किए गए I/O को अलग रखें) स्ट्रिंग पार्सिंग है। चूंकि आपके पास "," एक डिलीमीटर के रूप में प्रतीक है, इसलिए आप स्ट्रिंग पर एक रैखिक स्कैन कर सकते हैं और "\ 0" (एंड-स्ट्रिंग मार्कर, शून्य टर्मिनेटर) द्वारा सभी "," को प्रतिस्थापित कर सकते हैं।

कुछ इस तरह:

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

मैं अगर यह स्वीकार किए जाते हैं जवाब की तुलना में तेज हो जाएगा पता नहीं है, लेकिन मैं भी यह वैसे भी मामले में आप इसे करने की कोशिश करना चाहते हैं में पोस्ट कर सकता है। आप fseek magic. का उपयोग कर फ़ाइल के आकार को जानकर एक एकल पठन कॉल का उपयोग कर फ़ाइल की पूरी सामग्री में लोड कर सकते हैं यह एकाधिक पढ़ने वाली कॉल से बहुत तेज होगा।

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