web-dev-qa-db-ja.com

C ++でCSVファイルデータを読み取って操作するにはどうすればよいですか?

かなり一目瞭然で、私はグーグルを試し、多くの恐ろしい専門家の交換を受けました。ここでも検索できませんでした。オンラインチュートリアルまたは例が最適です。みんなありがとう。

51
zkwentz

あなたが本当にやっていることが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処理を2つの操作と考えてください。

_// 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行ずつ読み取りおよび書き込みを行います。明らかな利点がいくつかあります。

  1. データは、現在のソリューション(CSVファイル)ではなく、問題(顧客)にとって意味のある形式で表されます。
  2. バルクSQLインポート/エクスポート、Excel/OOスプレッドシートファイル、またはHTML _<table>_レンダリングなど、他のデータ形式用のアダプターを簡単に追加できます。
  3. メモリフットプリントは小さくなる可能性があります(相対的なsizeof(Customer)と1行のバイト数に依存します)。
  4. CSVReaderCSVWriterは、パフォーマンスや機能を損なうことなく、メモリ内モデル(Nelsonなど)の基盤として再利用できます。その逆は真実ではありません。
9
Tom

より多くの情報が役立ちます。

しかし、最も単純な形式:

#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++のCSVパーサー

56
Martin York

Boost Tokenizerライブラリ、特に Escaped List Separator を試すことができます

21

私は多くのCSVファイルを使用してきました。アドバイスを追加したい:

1-ソース(Excelなど)に応じて、フィールドにコンマまたはタブが埋め込まれる場合があります。通常、「ボストン、マサチューセッツ州02346」のように、フィールドが二重引用符で区切られるため、ルールは「保護」されます。

2-一部のソースでは、すべてのテキストフィールドが二重引用符で区切られません。他のソースはそうします。他のものは、数値であってもすべてのフィールドを区切ります。

3-二重引用符を含むフィールドは、通常、埋め込まれた二重引用符を2倍にします(「George "" Babe "" Ruth」のように、二重引用符で区切られたフィールド自体です。

4-一部のソースにはCR/LFが埋め込まれます(Excelもその1つです!)。時々、それはただのCRになるでしょう。通常、フィールドは二重引用符で区切られますが、この状況を処理するのは非常に困難です。

8
Marc Bernier

これはあなた自身が取り組むための良い練習です:)

ライブラリを3つの部分に分ける必要があります

  • CSVファイルの読み込み
  • 変更して読み取ることができるように、メモリ内のファイルを表現する
  • CSVファイルをディスクに保存し直す

したがって、次のものを含むCSVDocumentクラスの作成を検討しています。

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

ここから空欄に記入するだけです:)

これを言うとき私を信じてください-ライブラリ、特にデータの読み込み、操作、保存を扱う方法を学ぶためにあなたの時間を費やすことは、そのようなライブラリの存在への依存を取り除くだけでなく、より良いプログラマーの周り。

:)

[〜#〜] edit [〜#〜]

文字列の操作と解析について、あなたがどれだけ知っているかわかりません。あなたが動けなくなったら私は喜んで手伝います。

7
user19302

使用できるコードを次に示します。 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
Ashish Jain

ブーストトークナイザーを使用してレコードを解析する詳細はこちらを参照

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

Kernighan&Pikeによる「 The Practice of Programming 」(TPOP)をご覧ください。 CおよびC++の両方でCSVファイルを解析する例が含まれています。ただし、コードを使用しなくても、本を読む価値はあります。

(前のURL: http://cm.bell-labs.com/cm/cs/tpop/

2

私はこの興味深いアプローチを見つけました:

CSV to C構造ユーティリティ

引用:CSVtoCは、入力としてCSVまたはコンマ区切りファイルを受け取り、C構造体としてダンプするプログラムです。

当然、CSVファイルに変更を加えることはできませんが、データへの読み取り専用のメモリ内アクセスのみが必要な場合は、機能します。

0
Kevin P.