csv,全称Comma-Separated Values,可以直接以文本打开,也可以以用excel打开。一般使用逗号作为分隔标志。
下图为csv文件以excel中打开的样式。
下图为csv文件以文本方式打开的样式。
可以看到,如果在一个excel单元格中的数据,如果存在逗号或者双引号,那么该数据在文本数据中会将该数据以双引号引用起来。如果excel中的数据本身具有双引号,那么在文本数据中会为该双引号匹配多一个双引号。
因此,我们直接使用ifstream
等读方式是以文本方式读取csv文件数据的。如果我们想从csv文件中读取每个单元格真实显示的数据,必须对逗号及其双引号做处理。
处理代码如下:
#include <stack>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
void deal_special_character(std::string& s, const std::string& src, const std::string& des) {
int pos = 0;
while((pos = s.find(src, pos)) != std::string::npos) {
s = s.replace(pos, src.length(), des);
pos += des.length();
}
}
int main() {
std::ifstream infile("test.csv");
std::string lineStr;
while(std::getline(infile, lineStr)) { //读取csv中的每一行
std::vector<std::string> row;
int len = lineStr.length();
const std::string str = ",\"";
int startPos = 0;
int endPos = 0;
while(true) { //每次循环处理一个单元格的数据
if((endPos = lineStr.find_first_of(str, startPos)) == std::string::npos) { //最后一个单元格
std::string s = lineStr.substr(startPos);
row.push_back(s);
break;
}
if(lineStr[endPos] == ',') { //当前单元格没有逗号以及引号
std::string s = lineStr.substr(startPos, endPos - startPos);
row.push_back(s);
}else { //当前单元格存在逗号或者引号
std::stack<char> stk;
stk.push(lineStr[endPos]);
startPos = endPos; //记录要保存的字段当前位置
while(true) {
if((endPos = lineStr.find_first_of(str, endPos + 1)) == std::string::npos) { //最后一个单元格
std::string s = lineStr.substr(startPos + 1, len - 3 - startPos); //去掉头尾双引号以及行尾所特有的CR符号
deal_special_character(s, "\"\"", "\"");
row.push_back(s);
break;
}
if(lineStr[endPos] == ',' ) {
if(stk.empty()) {
std::string s = lineStr.substr(startPos + 1, endPos - 2 - startPos);//去掉头尾双引号
deal_special_character(s, "\"\"", "\"");
row.push_back(s);
break;
}
}
else if(stk.empty()) {
stk.push(lineStr[endPos]);
}else {
stk.pop();
}
}
}
if(endPos == std::string::npos)
break;
startPos = endPos + 1;
}
for(const std::string& e : row) {
std::cout << e << std::endl;
}
std::cout << std::endl;
}
return 0;
}
结果显示: