C言語でCSVファイルの行数を読み取りたい

このQ&Aのポイント
  • C言語でCSVファイルの行数を効率的に読み取る方法
  • CSVファイルの行数を取得して、動的にメモリを確保する方法を考えたい
  • CSVファイルの行数を取得して、プログラムをスマートに簡潔にする方法
回答を見る
  • ベストアンサー

C言語でCSVファイルの行数を読み取りたい

大学で研究のため,プログラミングをしている者です. 現在、外部CSVに実験記録名(SECTION)を記述し,それを読み込んでINIファイルからデータのパラメータ(KEY)を呼び出してに実験システムを動作させています. プログラムは以下のような構成になっています.都合上、省略してます. ------------------------------------------------------------------------- // 変数や構造体の宣言 typedef struct Parameter{ int a; double b; ... // パラメータが続きます } Parameter; Parameter data_value[DATA]; //実験に使用したパラメータの構造体 char data_name[MAX_PATH]; //実験記録名 #define DATA データ数 // 読み出し部 for( int i<0; i<DATA; i++ ){ fscanf( fp, "%s", &data_name ); // EOFを用いたエラーチェックを省略 data_value[i] = getParameter( data_name ); // セクション名を渡して、実験パラメータ構造体を返す関数 } ------------------------------------------------------------------------- CSVファイルは以下のように実験記録日と時間が記述されたものになります。 ------------------------------------------------------------------------- 2012/09/02_10:10:10 2012/09/09_14:10:10 ..... ------------------------------------------------------------------------- INIファイルは以下のように実験記録日と時間をセクション、そのときのパラメータをキーとしたものです. ------------------------------------------------------------------------- [2012/09/02_10:10:10] a = 100 b = 61.2 .... [2012/09/09_14:10:10] ..... ------------------------------------------------------------------------- ただ、この手法だと実験記録をINIファイルとCSVファイルに増やしていくたび(プログラムで処理)に、DATAを変更する必要があり、面倒です。(ちにみにいい結果が得られたときのみ記録するソフトになってます。) 私としては、定数DATAを使わず、CSVファイルから行数を取得したいと思ってます。 そして、パラメータは Parameter *param = new Parameter[;取得した行数]; のように動的に確保してINIファイルからデータを読み出したいと思ってます. そこで、あらかじめCSVファイルの行数を取得して、for文の最大値や動的なparamの生成をしたのですが、良い方法はないでしょうか? 友人からは、scanfを2回使って、1回目で行数獲得、獲得した値に基づき動的生成、2回目で実験記録名の読み取りをすればいいといわれたのですが、プログラムとして不細工な気がします。 この手法以外で提案がありましたら、教えていただきたいです。 質問文が長くなってしまい、申し訳ございません. ご回答よろしくお願い致します.

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

newはC言語に無いですが、同じ事をしたいなら Parameter *param = calloc(取得した行数, sizeof(Parameter)); ですね。 newというのをC++のつもりで言っているなら、やりたいことを考えるとnewで一括で確保するより、STLのvectorを使ったほうがいいですね。以下、C言語のほうメインで書きます。 > そこで、あらかじめCSVファイルの行数を取得して、for文の最大値や動的なparamの生成をしたのですが、良い方法はないでしょうか? 「"あらかじめ"CSVファイルの行数を取得する」ということをするには、CSVファイルの行数を何らかの方法で数えなくてはなりません。 改行というのはファイルの中に改行を示す文字を入れているだけなので、改行の回数を数えるためには、一度ファイルすべてを読まなくてはなりません。つまり、ご友人の仰る通り行数を調べるためのファイル読み込み、データを入れるためのファイル読み込みの2回のファイル読み込みが必要になります。 実装方針を変えると、1回でも可能になります。 「CSVファイルから実験記録を読みながら不足していたらデータを保存する容量を増やしていく」という方針です。 この場合、配列を使う実装とリストを使う実装の2種類の実装方針があると思います。配列の添字でデータを参照することが多い場合は配列、任意の箇所への挿入や削除が頻繁に行われる場合はリストを使うというのは常識ですが、今回の場合、前者だと予測するので配列での実装になります。もし、リストを使うほうが適切な場合はqueue.hを使うと楽に実装できます。といっても、添字でアクセスしているところはすべてリストを所定の回数たぐるという操作に書きなおしですが。 (参考: http://www.freebsd.org/cgi/cvsweb.cgi/~checkout~/src/sys/sys/queue.h http://www.jp.freebsd.org/cgi/mroff.cgi?sect=3&subdir=man&lc=1&cmd=&dir=jpman-7.2.2/man&man=LIST_HEAD) ファイルを読みながらデータを保存する容量を増やし、配列を使う場合、プログラムはこんなかんじになるでしょう。(コンパイルしてみてないのでコンパイルできるかすら保証しませんが) Parameter *data_value = NULL; //実験に使用したパラメータの構造体 size_t num_data_value = 0, max_data_value = 0; #define INCREASE_DATA_VALUE_SIZE 1024 char data_name[MAX_PATH]; //実験記録名 // 読み出し部 for (; !feof(fp); num_data_value++){ if (num_data_value >= max_data_value) { // data_valueの容量がなくなったら拡張 max_data_value += INCREASE_DATA_VALUE_SIZE; data_value = realloc(data_value, max_data_value * sizeof(data_value[0])); if (data_value == NULL) { fprintf(stderr, "Error: cannot allocate memory."); exit(EXIT_FAILURE); } } fscanf( fp, "%s", &data_name ); data_value[num_data_value] = getParameter( data_name ); // セクション名を渡して、実験パラメータ構造体を返す関数 } 以降はnum_data_valueをDATAの代わりに使います。 data_valueを関数で渡すときにはnum_data_valueも一緒に渡すことになります。 普通はdata_value、num_data_value、max_data_valueの3つ組みで構造体を作るかもしれません。 しかしながら、C言語でプログラムを書くには色々とおまじないが多いですし、メモリー周りの問題も簡単に起こせてしまうので個人的にはデータの集計にはPerl Python Rubyなどを使うほうがおすすめでです。今時、PCの性能は昔よりもずっと上がっていますし、スクリプト言語にもJITが実装されているのでファイルI/Oバリバリなことでもさせない限り、そう遅くないですしね。とは言っても、過去の先輩が残していった大量のライブラリーがあったりするとおいそれとスクリプト言語に行く事もできないと思いますが。その場合も、C++を使うことにして、Standard Template Library (STL)を使うと多少は楽になるかもしれません。例えばvectorを使えば何も考えずにメモリーの動的確保から容量の管理、自動的に追加をやってもらえますし、vectorは配列として取り出せますから。 というわけで、C言語で書く場合は不足するたびにreallocでメモリーを確保したら良いと思います。でも、データの集計にはスクリプト言語を使ったほうが楽です。過去の資産がある場合、実はC++を使って、extern "C"でそれを呼び出すようにして、残りはSTLを使うと多少は楽かもしれません。

apollograffitti
質問者

補足

ご回答ありがとうございます. 丁寧な回答で、とても勉強になりました. ちなみに使用している言語はC++で,質問タイトルは間違ってます.すみませんでした. C++の場合ですとvectorを用いた以下のような記述がベターになるでしょうか? --------------------------------------------------- vector<Parameter> data_value; // 動的配列 while(fscanf(fp,"%s",&data_name)!=EOF){ data_value.push_back(getParameter( data_name )); } ---------------------------------------------------

その他の回答 (5)

回答No.6

--------------------------------------------------- vector<Parameter> data_value; while(fscanf(fp,"%s",&data_name)!=EOF){ data_value.push_back(getParameter( data_name )); } --------------------------------------------------- はい。C++だとこういう記述のほうがいいと思います。 既にお分かりだと思いますが、DATAと書いているところの代わりにdata_value.size()を使います。 #5さんのおっしゃるような形で効率を求めるなら、vectorを使う場合はcapacityで予め使いそうな容量を宣言するという手がありますね。 STL全面禁止というコーディング規約も世の中にはあると聞きますが、ことメモリーの管理についてはvectorやstring、smart pointerを使ったほうが圧倒的に不具合が減るのでこういうところはどんどん使うべきだと思います。

apollograffitti
質問者

お礼

ご回答いただき,ありがとうございます.

回答No.5

reallocで確保サイズを変更していくのが無難という話に違いはありませんが 今回のケースだと、CSVの形式が決まっているようなのでファイルサイズから割り算である程度のサイズは確保できるのでそんな方法も検討してみるとよいのではないでしょうか。 たとえば、 例に挙がっている物だと、1レコードのバイト数は20バイト+改行コードです。 数値の部分が1桁の場合を考慮して、LF改行だと仮定すると最小レコード長は15バイト。 ということで必要な配列数の期待値は ファイルサイズ/15 個ですね。 ファイルサイズは stat() 関数とstat.st_sizeで取得できます。 # ほんとは オープンしたfp からfdを取得してfstatするのがいい。 reallocは、使い方を覚えて損のない関数ですけど、処理の効率を求めるのならば 定数的な計算量になる方法を考えてみるとよいと思います。 あと、reallocはあとで確保領域を縮小する感じで使った方がよいかもしれません。 フラグメント化しないように工夫はされているようですが細かく確保を繰り返すと あまり効率はよくないです。

回答No.3

C言語がまるで,動的に配列が確保できないかのような回答もあるので 一応言っておくと,参考URLのようにできるので, EOFにならない限り,動的に構造体のメモリ領域を生成していけば良いのでは.

参考URL:
http://itpro.nikkeibp.co.jp/article/COLUMN/20061128/255135/
  • ki073
  • ベストアンサー率77% (491/634)
回答No.2

動的に配列を確保できるプログラミング言語があるのですが、それはもう少し複雑な処理をしているようですが、基本的にはNo.1で書かれているようにlist構造を使っているようです。 プログラムの簡単さからいうと、質問者さんが書かれているように最初に行数を数えて、配列要素を確保してからの方が良いように思います。 余談ですが データ処理をするためにプログラムを自作することがよく有るのですが、普段はRubyを使っています。先に書いたように動的に配列を確保できる言語です。配列の要素数を宣言する必要がありませんので、ともかくデータを読みつつ、状況に応じて配列要素を追加していく方法が簡単にとれます。 いろいろデータを突っ込んだ「配列」をそのまま処理してもいいし、処理速度を考慮してCやFORTRAN型の配列に変換してから計算することもできます。この前簡単なプログラムでCと比べてみたのですが、全体の計算速度が1/10程度に遅くなるだけで、十分な速度が得られています。

  • nag0720
  • ベストアンサー率58% (1093/1860)
回答No.1

配列ではなく、リンクを使ったリスト型のデータ構造にしてはどうですか。

apollograffitti
質問者

お礼

ご回答いただきありがとうございます. リスト型のデータ構造を初めて知ったので,大変勉強になりました.

関連するQ&A

  • CSVファイルのデータの行数を取得したい

    こちらではいつもお世話になっています。Perlに関して、初心者ですが教えてください。 CSVファイルについて、データの存在する行数を取得したいと考えています。自分なりに考えたところでは、下記の方法で取得できるのではと思ったのですが・・・・・ open(FH,"data.csv"); @array = <FH>; $count = $#array; close(FH); data.csvは、1行目から順にデータが入っています。 これで、$countに1を足せばCSVデータの行数になるのではないかと考えています。 しかし、実際に動かしてみると、$countには、data.csvにデータがあるのに「-1」(要素なし)が返ってきます。何か間違いがあるのでしょうか。 あるいは、別にCSVデータの行数を取得する方法が他にあれば、教えていただけないでしょうか。 よろしくお願いします。

    • ベストアンサー
    • Perl
  • CSVデータの行数カウント

    PHP初心者です。 アドバイスよろしくお願いします。 CSVデータ 20060802:1,AAA,少し 20060802:2,AAA,少し 20060802:3,AAA,少し 20060802:4,BBB,大きい 20060802:5,AAA,小さい 20060802:6,AAA,小さい 20060802:7,AAA,小さい があるとき、 そのCSVデータを読み込んで データの3列目を基準としてデータがいくつあるかを数えたい。 例えば 少し・・・・3 大きい・・・・1 小さい・・・・1 というふうに数えたい。 $filename = CSVデータファイル; $fp = fopen("$filename", "r"); if($fp == false){ exit; } else { for$i = 0; $i < sizeof($fp); $i++){ $line = explode(",", $fp[$i]); // ここで // $i[2]==AAA // のとき、AAAの行数をカウントする処理をしたい。 } }

    • ベストアンサー
    • PHP
  • 行数の多い(65536行以上)csvファイルからピボットを作りたいので

    行数の多い(65536行以上)csvファイルからピボットを作りたいのですが、どうしたらよいでしょうか? 友人に、 1.アクセスにcsvファイルをとりこむ 2.エクセルのピボット作成で、外部データソースの取り込みを選ぶ 3.アクセスデータベスを選ぶ 4.アクセスのファイルのある場所を選んで、取り込んだファイルを選択 5.適当に「次へ」ですすんで、「完了」 でできるといわれたのですが、最後の「完了」の段階で、 「From句の構文エラー」 となってデータが取り込めません。 どなたか教えていただけないでしょうか。上記の方法でなくとも、行数の多いcsvファイルからピボットが作成できればいいのですが。

  • CSVの行数を取得したい

    VB6.0で開発しています。 カンマ区切りの""で囲まれたcsvファイルがあります。 例) "1","東京本社","","おとこ","山田太郎","" このデータをinsertしていくプログラムを標準モジュールで書きました。 正常に動いています。 処理件数を表示する画面をつくりたいのですが、タイマーコントロールにどう書けばよいでしょうか? Private Sub tmrSql_Timer() lblSqlProgress.Caption = insert済みの件数 & "/" & csvファイルの行数 & "の処理が終了しました" End Sub 上記のように記述すればよいのかな、と思っています。で、 1.insert済みの件数 2.csvファイルの行数 ってどうすればわかるんでしょう?? ご存知の方教えてください。 よろしくお願いします。

  • csvファイルのデータを変数として取込むには?

    javascriptは1年生です^^ データバインドを利用して、テーブルにデータを表示しています。 document.write("<object id='setData' classid='clsid:333C7BC4-460F-11D0-BC04-0080C7055A83'>"); document.write("<param name='DataURL' value='setdata.csv'>"); document.write("<param name='UseHeader' value='true'>"); document.write("</object>"); document.write("<table border='1' datasrc='#setData'>"); document.write("<tr>"); document.write("<td><span datafld='data1'></span></td>"); とこんな感じで続くのですが、csvファイルのデータには、セル幅や背景色等の設定値も含まれています。 これらのデータは表示用のデータではないので、変数として取込まなければなりません。 普通なら、bodyタグの中に、 <input type="hiden" datafld="optionData1"> と書いて、そこからjavascriptで取込むことは可能ですが、document.writeを使うとbodyタグの中が書き換わってしまうので使えません。 (csvファイルの1行分のデータを、HTMLのテーブルで表示する時には、4行~6行と行数が変動するので、予めテーブルをbodyタグの中に書いておくことが出来ません。テーブル行数はスクリプト内で決定するので。なのでbodyタグの中は空でオールjavascriptでHTMLを生成しています) もう一つの方法はフレームを使って、parentで、データを送ることも考えたのですが、もっとスマートな方法はないでしょうか??

  • 行数の変動にも対応したファイル読み込みのやり方

    学校でファイル読み込みの課題を今しているのですが、ファイルの行数が増えた場合にも対応していて、且つ無駄な領域を使わないようなファイル読み込みをするのに困っています。 流れとしては csvファイルから内容を読み込む ↓ カンマで分割し、構造体に格納 ↓ その格納した構造体を返す という動きをしたいのですが、構造体をmallocで動的にメモリ確保する時にも、行数の取得が必要になってきて・・・ 一度行数を調べてから、処理を行う方法を取るか、他のchar型配列か何かに一度全てを格納して、そのときにカウントした行数を使って構造体のメモリを動的に確保する・・・くらいまでは思いついたのですが、始め全てを格納する時点でもまた動的にメモリを格納する方法が思いつかなくて・・・・ やはりどこかで多めに領域を取り、そこに格納する手を使うしかないのでしょうか? ご教授お願いします。

  • CSVファイル読み込み

    VC++でCSVファイルの読み込みを作っています。 ファイル名「abc.csv」というファイルがあり CSVファイルの内容は、 "id","food" "1","バナナ" "2","オレンジ" となっています。 ソース上はファイルをオープンして 読み込むところまでは分かりましたが、 CSVファイルのコンマの分解とその後の処理が分かりません。読み込んだ後、コンボボックスで、CSVファイルのname一覧を表示する予定でいます。 例では、バナナ     オレンジ とコンボボックスで表示して選択できるようにします。 今のソースは以下の通りです。 CStdioFile file; CString name; int flag;  if(!file.Open("abc.csv",CFile::modeRead)){ AfxMessageBox("File Open failed"); } while(flag){ if(file.ReadString(name)==FALSE) //nameにデータ読み込み break; //CSVコンマを分解 //テキスファイルで読み込み?表示? } file.Close(); //file close 初心者なので間違いだらけかもしれませんが よろしくお願いしますm(__)m

  • csvファイルをAccessに取り込むとデータ数が増える

    項目をカンマで区切っただけの単純なcsvファイルをAccessでインポートしてテーブルを作成したら、データ数が増えてしまいました。 (csvファイルをテキストで開いたときの行数と、Accessの行数が異なる) 5000件ぐらいなら、csvもAccessもデータ数は同じなのですが、データ数が大きくなると、csvとAccessのテーブルで6万件とかの差分が出てしまいます。 csvファイルとAccessで、データ数を同じにするにはどうしたら良いでしょうか。 参考になるURLでも良いので、教えて頂ければと思います。 よろしくお願いします!

  • C言語でのCSVファイルの読み出し方法

    C言語の勉強をしています。 test.csvというファイル名のCSVファイルで 項目,名前,身長,体重,血液型,合否(合格なら○不合格なら空欄) 1,太郎,150,55.6,A,○ 2,二郎,165.5,60,B 3,三郎,160.2,59.5,AB,○ と書かれたファイルを読もうと思いまして #include <stdio.h> #define i 1000 #define j 1000 main() { int d[i][j]; double... FILE *fp; fp=fopen(fp,"test.csv","r"); for(i=0;i<=2;i++) { for(j=0;j<=6;j++) { fscanf(fp,"%d",d[i][j]); } }... という感じで書いても読み込みません。 どのように書いたら読み込むでしょうか。 Cをはじめて間もないのでどなたか教えていただけないでしょうか よろしくお願いします。

  • C言語のファイル読み込み

    あるcsvファイルがあり、それは以下のように(規則性のない)波形のデータです。 0.001 0.14 0.002 0.32 0.003 0.46 ・ ・ 2.000 0.22 左側がx軸、右側がy軸で、データはそれぞれ2000個あります。 このデータ(波形)をC言語のプログラムに読み込ませるにはどうしたらいいのでしょうか? #include <stdio.h> int main(void) { double i,j; FILE *file; file = fopen("sample.csv","r"); fscanf(file,"%ls,%ls",&i,&j); fclose(file); printf("i = %d : j = %d\n",i,j); return 0; } とすれば最初の1行目だけは読めますが、それ以降がわかりません。 配列を使えばいいのでしょうか?