我需要在c++中加载和使用CSV文件数据。在这一点上,它实际上只是一个以逗号分隔的解析器(即不用担心转义新行和逗号)。主要需要的是逐行解析器,它将在每次调用方法时为下一行返回一个向量。
我发现这篇文章看起来很有前途: http://www.boost.org/doc/libs/1_35_0/libs/spirit/example/fundamental/list_parser.cpp
我从未使用过Boost's Spirit,但我愿意尝试一下。但前提是我忽略了一个更直接的解决方案。
我需要在c++中加载和使用CSV文件数据。在这一点上,它实际上只是一个以逗号分隔的解析器(即不用担心转义新行和逗号)。主要需要的是逐行解析器,它将在每次调用方法时为下一行返回一个向量。
我发现这篇文章看起来很有前途: http://www.boost.org/doc/libs/1_35_0/libs/spirit/example/fundamental/list_parser.cpp
我从未使用过Boost's Spirit,但我愿意尝试一下。但前提是我忽略了一个更直接的解决方案。
当前回答
该解决方案检测这4种情况
完整的课程在
https://github.com/pedro-vicente/csv-parser
1,field 2,field 3,
1,field 2,"field 3 quoted, with separator",
1,field 2,"field 3
with newline",
1,field 2,"field 3
with newline and separator,",
它一个字符一个字符地读取文件,每次读取一行到一个向量(字符串),因此适合于非常大的文件。
使用
迭代直到返回空行(文件结束)。行是一个向量,其中每个条目都是一个CSV列。
read_csv_t csv;
csv.open("../test.csv");
std::vector<std::string> row;
while (true)
{
row = csv.read_row();
if (row.size() == 0)
{
break;
}
}
类声明
class read_csv_t
{
public:
read_csv_t();
int open(const std::string &file_name);
std::vector<std::string> read_row();
private:
std::ifstream m_ifs;
};
实现
std::vector<std::string> read_csv_t::read_row()
{
bool quote_mode = false;
std::vector<std::string> row;
std::string column;
char c;
while (m_ifs.get(c))
{
switch (c)
{
/////////////////////////////////////////////////////////////////////////////////////////////////////
//separator ',' detected.
//in quote mode add character to column
//push column if not in quote mode
/////////////////////////////////////////////////////////////////////////////////////////////////////
case ',':
if (quote_mode == true)
{
column += c;
}
else
{
row.push_back(column);
column.clear();
}
break;
/////////////////////////////////////////////////////////////////////////////////////////////////////
//quote '"' detected.
//toggle quote mode
/////////////////////////////////////////////////////////////////////////////////////////////////////
case '"':
quote_mode = !quote_mode;
break;
/////////////////////////////////////////////////////////////////////////////////////////////////////
//line end detected
//in quote mode add character to column
//return row if not in quote mode
/////////////////////////////////////////////////////////////////////////////////////////////////////
case '\n':
case '\r':
if (quote_mode == true)
{
column += c;
}
else
{
return row;
}
break;
/////////////////////////////////////////////////////////////////////////////////////////////////////
//default, add character to column
/////////////////////////////////////////////////////////////////////////////////////////////////////
default:
column += c;
break;
}
}
//return empty vector if end of file detected
m_ifs.close();
std::vector<std::string> v;
return v;
}
其他回答
不好意思,但是为了隐藏几行代码,这似乎是非常复杂的语法。
为什么不这样呢:
/**
Read line from a CSV file
@param[in] fp file pointer to open file
@param[in] vls reference to vector of strings to hold next line
*/
void readCSV( FILE *fp, std::vector<std::string>& vls )
{
vls.clear();
if( ! fp )
return;
char buf[10000];
if( ! fgets( buf,999,fp) )
return;
std::string s = buf;
int p,q;
q = -1;
// loop over columns
while( 1 ) {
p = q;
q = s.find_first_of(",\n",p+1);
if( q == -1 )
break;
vls.push_back( s.substr(p+1,q-p-1) );
}
}
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<std::string> vls;
FILE * fp = fopen( argv[1], "r" );
if( ! fp )
return 1;
readCSV( fp, vls );
readCSV( fp, vls );
readCSV( fp, vls );
std::cout << "row 3, col 4 is " << vls[3].c_str() << "\n";
return 0;
}
使用流解析CSV文件行
我写了一个解析CSV文件行的小例子,如果需要,它可以用for和while循环来开发:
#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;
int main() {
ifstream fin("Infile.csv");
ofstream fout("OutFile.csv");
string strline, strremain, strCol1 , strout;
string delimeter =";";
int d1;
继续到文件的末尾:
while (!fin.eof()){
从InFile获取第一行:
getline(fin,strline,'\n');
在直线上找到度距仪的位置:
d1 = strline.find(';');
然后解析第一列:
strCol1 = strline.substr(0,d1); // parse first Column
d1++;
strremain = strline.substr(d1); // remaining line
创建CSV格式输出行:
strout.append(strCol1);
strout.append(delimeter);
写行到输出文件:
fout << strout << endl; //out file line
}
fin.close();
fout.close();
return(0);
}
代码已编译并运行。好运!
你可以在escaped_list_separator中使用Boost Tokenizer。
Escaped_list_separator解析csv的超集。Boost::记号赋予器
这只使用Boost标记器头文件,不需要链接到Boost库。
下面是一个例子,(详情请参阅c++中使用Boost Tokenizer解析CSV文件或Boost:: Tokenizer):
#include <iostream> // cout, endl
#include <fstream> // fstream
#include <vector>
#include <string>
#include <algorithm> // copy
#include <iterator> // ostream_operator
#include <boost/tokenizer.hpp>
int main()
{
using namespace std;
using namespace boost;
string data("data.csv");
ifstream in(data.c_str());
if (!in.is_open()) return 1;
typedef tokenizer< escaped_list_separator<char> > Tokenizer;
vector< string > vec;
string line;
while (getline(in,line))
{
Tokenizer tok(line);
vec.assign(tok.begin(),tok.end());
// vector now contains strings from one row, output to cout here
copy(vec.begin(), vec.end(), ostream_iterator<string>(cout, "|"));
cout << "\n----------------------" << endl;
}
}
该解决方案检测这4种情况
完整的课程在
https://github.com/pedro-vicente/csv-parser
1,field 2,field 3,
1,field 2,"field 3 quoted, with separator",
1,field 2,"field 3
with newline",
1,field 2,"field 3
with newline and separator,",
它一个字符一个字符地读取文件,每次读取一行到一个向量(字符串),因此适合于非常大的文件。
使用
迭代直到返回空行(文件结束)。行是一个向量,其中每个条目都是一个CSV列。
read_csv_t csv;
csv.open("../test.csv");
std::vector<std::string> row;
while (true)
{
row = csv.read_row();
if (row.size() == 0)
{
break;
}
}
类声明
class read_csv_t
{
public:
read_csv_t();
int open(const std::string &file_name);
std::vector<std::string> read_row();
private:
std::ifstream m_ifs;
};
实现
std::vector<std::string> read_csv_t::read_row()
{
bool quote_mode = false;
std::vector<std::string> row;
std::string column;
char c;
while (m_ifs.get(c))
{
switch (c)
{
/////////////////////////////////////////////////////////////////////////////////////////////////////
//separator ',' detected.
//in quote mode add character to column
//push column if not in quote mode
/////////////////////////////////////////////////////////////////////////////////////////////////////
case ',':
if (quote_mode == true)
{
column += c;
}
else
{
row.push_back(column);
column.clear();
}
break;
/////////////////////////////////////////////////////////////////////////////////////////////////////
//quote '"' detected.
//toggle quote mode
/////////////////////////////////////////////////////////////////////////////////////////////////////
case '"':
quote_mode = !quote_mode;
break;
/////////////////////////////////////////////////////////////////////////////////////////////////////
//line end detected
//in quote mode add character to column
//return row if not in quote mode
/////////////////////////////////////////////////////////////////////////////////////////////////////
case '\n':
case '\r':
if (quote_mode == true)
{
column += c;
}
else
{
return row;
}
break;
/////////////////////////////////////////////////////////////////////////////////////////////////////
//default, add character to column
/////////////////////////////////////////////////////////////////////////////////////////////////////
default:
column += c;
break;
}
}
//return empty vector if end of file detected
m_ifs.close();
std::vector<std::string> v;
return v;
}
可以使用std::regex。
根据文件大小和可用内存,可以逐行读取,也可以完全在std::string中读取。
读取文件可以使用:
std::ifstream t("file.txt");
std::string sin((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
然后你可以和这个相匹配,它实际上是根据你的需要定制的。
std::regex word_regex(",\\s]+");
auto what =
std::sregex_iterator(sin.begin(), sin.end(), word_regex);
auto wend = std::sregex_iterator();
std::vector<std::string> v;
for (;what!=wend ; wend) {
std::smatch match = *what;
v.push_back(match.str());
}