[英]Fast, Simple CSV Parsing in C++
我試圖解析一個簡單的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
所以,一個非常簡單和固定的格式文件。 我將這些數據的每一列存儲到STL向量中。 因此我嘗試使用標准庫保持C ++方式,並且我在循環中的實現看起來像:
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);
問題是,這非常慢(每個數據文件有超過100萬行),在我看來有點不優雅。 有沒有更快的方法使用標准庫,或者我應該只使用stdio函數? 在我看來,整個代碼塊將減少為單個fscanf調用。
提前致謝!
使用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);
如果知道文件中的行數,可以在讀取之前調整向量的大小,然后使用operator[]
而不是at()
。 這樣就可以避免邊界檢查,從而獲得一點性能。
我認為主要的瓶頸(放棄基於getline()的非緩沖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
}
我不知道這是否會比接受的答案更快,但我還是可以發布它,以防你想嘗試一下。 通過使用一些fseek魔法知道文件的大小,您可以使用單個讀取調用加載文件的整個內容。 這將比多次讀取調用快得多。
然后你可以做這樣的事情來解析你的字符串:
//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], ",");
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.