我如何读取和操作C ++中的CSV文件数据?
相当不言自明,我试图谷歌,并得到了很多可怕的expertsexchange,我也在这里search,无济于事。 在线教程或例子将是最好的。 多谢你们。
如果你真正在做的是操纵一个CSV文件本身,尼尔森的答案是有道理的。 但是,我的怀疑是,CSV只不过是你正在解决的问题的一个神器。 在C ++中,这可能意味着你有这样的数据模型:
struct Customer { int id; std::string first_name; std::string last_name; struct { std::string street; std::string unit; } address; char state[2]; int zip; };
因此,在处理数据集合时, std::vector<Customer>
或std::set<Customer>
是有意义的。
考虑到这一点,将您的CSV处理视为两个操作:
// if you wanted to go nuts, you could use a forward iterator concept for both of these class CSVReader { public: CSVReader(const std::string &inputFile); bool hasNextLine(); void readNextLine(std::vector<std::string> &fields); private: /* secrets */ }; class CSVWriter { public: CSVWriter(const std::string &outputFile); void writeNextLine(const std::vector<std::string> &fields); private: /* more secrets */ }; void readCustomers(CSVReader &reader, std::vector<Customer> &customers); void writeCustomers(CSVWriter &writer, const std::vector<Customer> &customers);
一次读写一行,而不是保存文件本身的完整内存表示。 有几个明显的好处:
- 您的数据以适合您的问题(客户)的forms表示,而不是当前的解决scheme(CSV文件)。
- 您可以简单地为其他数据格式添加适配器,如批量SQL导入/导出,Excel / OO电子表格文件,甚至是HTML
<table>
渲染。 - 您的内存占用可能会更小(取决于
sizeof(Customer)
相对sizeof(Customer)
与单行中的字节数)。 -
CSVReader
和CSVWriter
可以在不损失性能或function的情况下作为内存模型(如Nelson's)的基础重用。 相反是不正确的。
更多的信息将是有用的。
但最简单的forms是:
#include <iostream> #include <sstream> #include <fstream> #include <string> int main() { std::ifstream data("plop.csv"); std::string line; while(std::getline(data,line)) { std::stringstream lineStream(line); std::string cell; while(std::getline(lineStream,cell,',')) { // You have a cell!!!! } } }
另请参阅以下问题: C ++中的CSVparsing器
您可以尝试Boost Tokenizer库,特别是Escaped List Separator
我在这段时间里使用了很多CSV文件。 我想添加build议:
1 – 根据来源(Excel等),逗号或制表符可能embedded在一个字段中。 通常情况下,他们将被“保护”,因为该字段将被双引号分隔,如“波士顿,MA 02346”。
2 – 有些来源不会双引号分隔所有文本字段。 其他来源将。 其他人将界定所有领域,甚至数字。
3 – 包含双引号的字段通常会将embedded的双引号加倍(而字段本身则用双引号分隔,如“George”“Babe”“Ruth”中所示。
4 – 一些来源将embeddedCR / LF(Excel是其中之一!)。 有时它只是一个CR。 这个字段通常会被双引号分隔,但是这种情况很难处理。
这对你自己来说是一个很好的练习:)
你应该把你的图书馆分成三部分
- 加载CSV文件
- 在内存中表示文件,以便您可以修改并读取它
- 将CSV文件保存回磁盘
所以你正在看编写一个CSVDocument类,其中包含:
- 加载(const char *文件);
- 保存(const char *文件);
- GetBody
所以你可以像这样使用你的库:
CSVDocument doc; doc.Load("file.csv"); CSVDocumentBody* body = doc.GetBody(); CSVDocumentRow* header = body->GetRow(0); for (int i = 0; i < header->GetFieldCount(); i++) { CSVDocumentField* col = header->GetField(i); cout << col->GetText() << "\t"; } for (int i = 1; i < body->GetRowCount(); i++) // i = 1 so we skip the header { CSVDocumentRow* row = body->GetRow(i); for (int p = 0; p < row->GetFieldCount(); p++) { cout << row->GetField(p)->GetText() << "\t"; } cout << "\n"; } body->GetRecord(10)->SetText("hello world"); CSVDocumentRow* lastRow = body->AddRow(); lastRow->AddField()->SetText("Hey there"); lastRow->AddField()->SetText("Hey there column 2"); doc->Save("file.csv");
这给了我们以下接口:
class CSVDocument { public: void Load(const char* file); void Save(const char* file); CSVDocumentBody* GetBody(); }; class CSVDocumentBody { public: int GetRowCount(); CSVDocumentRow* GetRow(int index); CSVDocumentRow* AddRow(); }; class CSVDocumentRow { public: int GetFieldCount(); CSVDocumentField* GetField(int index); CSVDocumentField* AddField(int index); }; class CSVDocumentField { public: const char* GetText(); void GetText(const char* text); };
现在你只需从这里填入空格:)
当我这样说的时候相信我 – 把你的时间投入到学习如何使图书馆,尤其是那些处理数据的加载,处理和保存的图书馆,不仅会消除你对这些图书馆的依赖,而且还会使你成为一个全能的图书馆,围绕着更好的程序员。
🙂
编辑
我不知道你已经知道了多lessstring操作和parsing; 所以如果你卡住了,我会很乐意帮忙。
这里是一些你可以使用的代码。 来自csv的数据存储在一个行数组中。 每一行都是一个string数组。 希望这可以帮助。
#include <iostream> #include <string> #include <fstream> #include <sstream> #include <vector> typedef std::string String; typedef std::vector<String> CSVRow; typedef CSVRow::const_iterator CSVRowCI; typedef std::vector<CSVRow> CSVDatabase; typedef CSVDatabase::const_iterator CSVDatabaseCI; void readCSV(std::istream &input, CSVDatabase &db); void display(const CSVRow&); void display(const CSVDatabase&); int main(){ std::fstream file("file.csv", std::ios::in); if(!file.is_open()){ std::cout << "File not found!\n"; return 1; } CSVDatabase db; readCSV(file, db); display(db); } void readCSV(std::istream &input, CSVDatabase &db){ String csvLine; // read every line from the stream while( std::getline(input, csvLine) ){ std::istringstream csvStream(csvLine); CSVRow csvRow; String csvCol; // read every element from the line that is seperated by commas // and put it into the vector or strings while( std::getline(csvStream, csvCol, ',') ) csvRow.push_back(csvCol); db.push_back(csvRow); } } void display(const CSVRow& row){ if(!row.size()) return; CSVRowCI i=row.begin(); std::cout<<*(i++); for(;i != row.end();++i) std::cout<<','<<*i; } void display(const CSVDatabase& db){ if(!db.size()) return; CSVDatabaseCI i=db.begin(); for(; i != db.end(); ++i){ display(*i); std::cout<<std::endl; } }
使用boost tokenizerparsinglogging , 请参阅这里获取更多细节 。
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()); /// do something with the record if (vec.size() < 3) continue; copy(vec.begin(), vec.end(), ostream_iterator<string>(cout, "|")); cout << "\n----------------------" << endl; }
看Kernighan&Pike 的“编程实践 ”(TPOP)。 它包括一个在C和C ++中parsingCSV文件的例子。 但即使不使用代码,也应该阅读本书。
(上一个URL: http : //cm.bell-labs.com/cm/cs/tpop/ )
我发现这个有趣的方法:
CSV到C结构实用程序
Quote:CSVtoC是一个程序,需要一个CSV或逗号分隔的值文件作为input,并将其转储为C结构。
当然,您不能对CSV文件进行更改,但如果您只需要对数据进行内存只读访问,则可以使用。