• 締切済み

[C,C++]テキストファイルの読み込み

はじめまして C++をはじめてまだ半年の初学者です。 テキストの読み込みについての質問なのですが、 以下のような形式(csv形式)のテキストのヘッダーと項目名を除く 数字の部分と文字の部分を読み込みたいのですが どのようにプログラムを書けばよいですか? ヘッダーと項目は不要です。 動物の名前とそのXYZは20個、日付は結構な数並んでいます。 時間と動物のデータは読み込み後、動物の種類ごとに構造体に格納します。 したがって、動物のデータはループで読み込めると良いのですが、 その方法が分かりません。 よろしくお願いします。 ************************** animal.txt by tanaka.tarou ************************** DATE, NAME, X, Y, Z, NAME, X, Y, Z, NAME, X, Y, Z, ・・・ 2011.05.23, rabit, 2, 5, 6, pig, 5, 3, 9, cow, 6, 3, 8, ・・・ 2011.05.23, rabit, 2, 5, 6, pig, 5, 3, 9, cow, 6, 3, 8, ・・・ 2011.05.23, rabit, 2, 5, 6, pig, 5, 3, 9, cow, 6, 3, 8, ・・・ 2011.05.23, rabit, 2, 5, 6, pig, 5, 3, 9, cow, 6, 3, 8, ・・・               ・               ・               ・

みんなの回答

回答No.3

// ざっくりこんなもんかしら。 // VC++ 10 でコンパイル/動作確認済 #include <iostream> #include <fstream> #include <set> #include <cstring> #include <string> #include <algorithm> using namespace std; struct record { string date; string name; int x, y, z; record() {} record(const string& n) : name(n) {} }; inline bool operator<(const record& x, const record& y) { return x.name < y.name; } ostream& operator<<(ostream& stream, const record& r) { return stream << r.date << ' ' << r.name << ':' << r.x << ',' << r.y << ',' << r.z; } inline string trim(const string& s) { return s.substr(s.find_first_not_of(" ")); } int main() { multiset<record> animals; set<string> names; ifstream stream("animal.txt"); if ( !stream.is_open() ) return 1; string line; bool skip = true; while ( getline(stream,line) ) { if ( skip ) { if ( line.substr(0,4) == "DATE" ) skip = false; } else { record rec; char* token; char* p = &line[0]; p = strtok(p,","); if ( !p ) break; rec.date = trim(p); while ( true ) { p = strtok(nullptr,","); if ( !p ) break; rec.name = trim(p); p = strtok(nullptr,","); if ( !p ) break; rec.x = atoi(p); p = strtok(nullptr,","); if ( !p ) break; rec.y = atoi(p); p = strtok(nullptr,","); if ( !p ) break; rec.z = atoi(p); names.insert(rec.name); animals.insert(rec); } } } stream.close(); for ( auto iter = names.begin(); iter != names.end(); ++iter ) { auto range = animals.equal_range(record(*iter)); for_each( range.first, range.second, [](const record& rec) { cout << rec << endl; }); cout << endl; } } #if 0 /* 以降を切り取って animal.txt とすべし。 */ ************************** animal.txt by tanaka.tarou ************************** DATE, NAME, X, Y, Z, NAME, X, Y, Z, NAME, X, Y, Z, ・・・ 2011.05.23, rabit, 2, 5, 6, pig, 5, 3, 9, cow, 6, 3, 8, sheep, 1, 2, 3 2011.05.23, rabit, 2, 5, 6, pig, 5, 3, 9, cow, 6, 3, 8 2011.05.23, rabit, 2, 5, 6, pig, 5, 3, 9, cow, 6, 3, 8, cat, 2, 3, 4 2011.05.23, rabit, 2, 5, 6, pig, 5, 3, 9, cow, 6, 3, 8, dog, 3, 4, 5 #endif

myuras
質問者

お礼

コード付きの回答ありがとうございます。 手持ちの古いBorlandコンパイラだとエラーをはくので、 後でVC++で試してみます。 コードを見て思ったのですが構造体を書き忘れていましたね、 重ねて感謝です。

回答No.2

こんな感じではどうですか? ファイルが終わるまで、一行まるまる読みこむ。(fgets推奨)    文字位置を何らかの形で持って、行頭にセット。    最初の文字が数字だったら、       頭から日付を解釈し、文字位置を','の次に進める。       文字位置の文字が行端でない間、          ','の前までを動物名として、取得。          文字位置を','の次に進める。          次はXを取得。          文字位置を','の次に進める。          次はY。          文字位置を','の次に進める。          次はZ。          文字位置に改行文字が来なければ、文字位置を','の次に進める。          ここまで取得した各情報を、構造体にセット。 幾つあるかわからない情報を持つには、構造体は動的に確保するのがいいでしょうね。

myuras
質問者

お礼

回答ありがとうございます おっしゃるとおりにfgetsやifstreamを使ってみたのですが、 やはりべた書きでした。 少しでもループで処理できたらと思っています。

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.1

そんなヘッダがあるのがcsvファイルと言えるか? と言う問題も…。 ヘッダ中に','があらわれることはない。 というのであれば… 1行ずつ読み込み、','が無い間はヘッダなので読み捨てる。 最初に','が含まれる行があったら項目名の行なので読み捨てる。 その後に読み込まれるのはデータが含まれる行なので読み込み後、各データを構造体へつっこむ。 ファイルの最後まで繰り返し… という感じに処理していくことになろうかと。 # ヘッダとやらに','が含まれる場合はこの方法は使えませんが。

myuras
質問者

お礼

回答ありがとうございます。 csvってヘッダー無いんですね、勉強になります。 1行読み込みの際に使うfscanfだと書式が必要になると思うのですが、 読み捨て部分と必要なデータ部分で書式が違う点はどのようにすればいいでしょうか? また、20個分の動物データの構造体への突っ込みは、 やはりべた書きでじゃないとできませんか? 多少、プログラムコードを交えてもらえると助かります

専門家に質問してみよう