当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在C++中读取和操作CSV文件数据?》经验,为你挑选了6个好方法。

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



1> Martin York..:

更多信息将是有用的.

但最简单的形式:

#include 
#include 
#include 
#include 

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++中的CSV解析器


如果你允许在单元格中使用逗号,可能通过引用单元格,转义逗号或两者都会变得更复杂.

2> Alessandro J..:

您可以尝试Boost Tokenizer库,特别是Escaped List Separator


引用的字符串不是边缘情况(除非你有隧道视觉)

3> Tom..:

如果您真正在做的是操纵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或者有意义std::set.

考虑到这一点,请将您的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 &fields);
private:
    /* secrets */
};
class CSVWriter {
public:
    CSVWriter(const std::string &outputFile);
    void writeNextLine(const std::vector &fields);
private:
    /* more secrets */
};
void readCustomers(CSVReader &reader, std::vector &customers);
void writeCustomers(CSVWriter &writer, const std::vector &customers);

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

    您的数据以对您的问题(客户)有意义的形式表示,而不是当前解决方案(CSV文件).

    您可以简单地为其他数据格式添加适配器,例如批量SQL导入/导出,Excel/OO电子表格文件,甚至是HTML

    呈现.

    您的内存占用量可能更小(取决于相sizeof(Customer)对于单行中的字节数).

    CSVReader并且CSVWriter可以在不损失性能或功能的情况下重用作内存模型(如Nelson的基础).相反的情况并非如此.



    4> Marc Bernier..:

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

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

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

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

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


    如果你这样做,你应该没问题 - http://tools.ietf.org/html/rfc4180

    5> 小智..:

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

    你应该将你的图书馆分为三个部分

    加载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);
    };
    

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

    相信我,当我这样说时 - 花时间学习如何制作图书馆,特别是那些处理数据加载,操作和保存的图书馆,不仅会消除你对这些图书馆存在的依赖,而且会让你成为一个全部 - 更好的程序员.

    :)

    编辑

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


    不,不要自己动手.使用经过良好测试的库.

    6> 小智..:

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

    #include 
    #include 
    #include 
    #include 
    #include 
    typedef std::string String;
    typedef std::vector CSVRow;
    typedef CSVRow::const_iterator CSVRowCI;
    typedef std::vector 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<

    推荐阅读
    oDavid_仔o_880
    这个屌丝很懒,什么也没留下!
    DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
    Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有