如何在C ++中读取和操作CSV文件数据?

时间:2009-01-06 04:55:55

标签: c++ csv

相当不言自明,我尝试谷歌并得到了很多可怕的expertsexchange,我在这里搜索也无济于事。在线教程或示例是最好的。谢谢你们。

9 个答案:

答案 0 :(得分:55)

更多信息会很有用。

但最简单的形式:

#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!!!!
        }
    }
 }

另请参阅此问题:CSV parser in C++

答案 1 :(得分:21)

您可以尝试使用Boost Tokenizer库,尤其是Escaped List Separator

答案 2 :(得分:9)

如果您真正在做的是操纵CSV文件本身,Nelson的回答是有道理的。但是,我怀疑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);

一次读取和写入一行,而不是保留文件本身的完整内存表示。有一些明显的好处:

  1. 您的数据以对您的问题(客户)有意义的形式呈现,而不是当前的解决方案(CSV文件)。
  2. 您可以轻松地为其他数据格式添加适配器,例如批量SQL导入/导出,Excel / OO电子表格文件,甚至是HTML <table>渲染。
  3. 您的内存占用量可能会更小(取决于相对sizeof(Customer)与单行中的字节数)。
  4. CSVReaderCSVWriter可以作为内存模型(例如Nelson's)的基础重用,而不会损失性能或功能。相反的情况并非如此。

答案 3 :(得分:8)

我在我的时间里使用过很多CSV文件。我想补充一下建议:

1 - 根据来源(Excel等),可以在字段中嵌入逗号或制表符。通常,规则是它们将被“保护”,因为该字段将被双引号分隔,如“Boston,MA 02346”。

2 - 某些来源不会双引号分隔所有文本字段。其他来源会。其他人会划定所有领域,甚至是数字。

3 - 包含双引号的字段通常会将嵌入的双引号加倍(字段本身用双引号分隔,如“George”“Babe”“Ruth”。

4 - 某些来源将嵌入CR / LF(Excel就是其中之一!)。有时它只是一个CR。该字段通常是双引号分隔,但这种情况很难处理。

答案 4 :(得分:7)

这对你自己来说是一个很好的练习:)

你应该把你的图书馆分成三个部分

  • 加载CSV文件
  • 将文件表示在内存中,以便您可以对其进行修改并阅读
  • 将CSV文件保存回磁盘

所以你正在考虑编写一个包含以下内容的CSVDocument类:

  • 加载(const char * file);
  • 保存(const char * file);
  • 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);
};

现在你只需填写这里的空白:)

当我这样说时,请相信我 - 花时间学习如何制作库,特别是那些处理数据加载,操作和保存的库,不仅会消除你对这些库存在的依赖,还会使你一个全能的更好的程序员。

:)

修改

我不知道你对字符串操作和解析有多了解;所以,如果你遇到困难,我很乐意提供帮助。

答案 5 :(得分:6)

以下是您可以使用的一些代码。来自csv的数据存储在行数组中。每行都是一个字符串数组。希望这会有所帮助。

#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;
  }
}

答案 6 :(得分:2)

看看Kernighan&amp ;;'The Practice of Programming'(TPOP)派克。它包括在C和C ++中解析CSV文件的示例。但即使您不使用该代码,也值得阅读本书。

(以前的网址:http://cm.bell-labs.com/cm/cs/tpop/

答案 7 :(得分:2)

使用提升标记器解析记录see here for more details

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

答案 8 :(得分:0)

我找到了这个有趣的方法:

CSV to C structure utility

引用: CSVtoC是一个程序,它将CSV或逗号分隔值文件作为输入并将其转储为C结构。

当然,您不能对CSV文件进行更改,但如果您只需要对数据进行内存中的只读访问,则可以正常工作。