かなり自明ですが、Googleを試してみたところ、恐ろしい専門家交換がたくさんありました。ここでも検索しましたが、役に立ちませんでした。オンライン チュートリアルまたは例が最適です。みんなありがとう。
9 に答える
より多くの情報が役立ちます。
しかし、最も単純な形式は次のとおりです。
#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 パーサー
あなたが実際に行っているのが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つの行を読み書きします。明らかな利点がいくつかあります。
- データは、現在のソリューション(CSVファイル)ではなく、問題(顧客)にとって意味のある形式で表されます。
- バルクSQLインポート/エクスポート、Excel / OOスプレッドシートファイル、さらにはHTML
<table>
レンダリングなど、他のデータ形式用のアダプターを簡単に追加できます。 - メモリフットプリントは小さくなる可能性があります(相対
sizeof(Customer)
対単一行のバイト数によって異なります)。 CSVReader
またCSVWriter
、パフォーマンスや機能を損なうことなく、メモリ内モデル(Nelsonなど)の基盤として再利用できます。その逆は真実ではありません。
私は自分の時代にたくさんのCSVファイルを扱ってきました。アドバイスを追加したいと思います:
1-ソース(Excelなど)によっては、フィールドにコンマまたはタブが埋め込まれている場合があります。通常、「ボストン、マサチューセッツ02346」のように、フィールドは二重引用符で区切られるため、「保護」されるという規則があります。
2-一部のソースでは、すべてのテキストフィールドを二重引用符で区切ることはできません。他の情報源はそうするでしょう。その他は、数値も含めてすべてのフィールドを区切ります。
3-二重引用符を含むフィールドは通常、埋め込まれた二重引用符を2倍にします(「George」「Babe」「Ruth」のように、フィールド自体は二重引用符で区切られます)。
4-一部のソースにはCR/LFが埋め込まれます(Excelはその1つです!)。時々それはただのCRになるでしょう。フィールドは通常、二重引用符で区切られますが、この状況を処理するのは非常に困難です。
これは、自分自身が取り組む良い練習になります:)
ライブラリを 3 つの部分に分割する必要があります
- CSV ファイルのロード
- ファイルを変更して読み取ることができるように、メモリ内のファイルを表す
- CSV ファイルをディスクに保存する
したがって、以下を含む CSVDocument クラスの作成を検討しています。
- Load(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);
};
ここから空欄を埋めるだけです:)
私がこれを言うとき、私を信じてください-ライブラリの作成方法、特にデータのロード、操作、および保存を扱う方法を学ぶことに時間を投資することは、そのようなライブラリの存在への依存を取り除くだけでなく、あなたをすべてにします-より良いプログラマーの周り。
:)
編集
文字列の操作と解析について、あなたがどれだけ知っているかはわかりません。行き詰まった場合は、喜んでお手伝いします。
ブースト トークナイザを使用してレコードを解析します。詳細については、こちらを参照してください。
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;
}
私はこの興味深いアプローチを見つけました:
引用: CSVtoC は、CSV またはコンマ区切り値ファイルを入力として受け取り、それを C 構造としてダンプするプログラムです。
当然、CSV ファイルを変更することはできませんが、データへのメモリ内読み取り専用アクセスのみが必要な場合は、機能する可能性があります。