プロが教える店舗&オフィスのセキュリティ対策術

大学で研究のため,プログラミングをしている者です.

現在、外部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回目で実験記録名の読み取りをすればいいといわれたのですが、プログラムとして不細工な気がします。
この手法以外で提案がありましたら、教えていただきたいです。

質問文が長くなってしまい、申し訳ございません.
ご回答よろしくお願い致します.

A 回答 (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を使ったほうが圧倒的に不具合が減るのでこういうところはどんどん使うべきだと思います。
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/09/28 18:18

reallocで確保サイズを変更していくのが無難という話に違いはありませんが


今回のケースだと、CSVの形式が決まっているようなのでファイルサイズから割り算である程度のサイズは確保できるのでそんな方法も検討してみるとよいのではないでしょうか。

たとえば、
例に挙がっている物だと、1レコードのバイト数は20バイト+改行コードです。
数値の部分が1桁の場合を考慮して、LF改行だと仮定すると最小レコード長は15バイト。

ということで必要な配列数の期待値は ファイルサイズ/15 個ですね。
ファイルサイズは stat() 関数とstat.st_sizeで取得できます。
# ほんとは オープンしたfp からfdを取得してfstatするのがいい。


reallocは、使い方を覚えて損のない関数ですけど、処理の効率を求めるのならば
定数的な計算量になる方法を考えてみるとよいと思います。
あと、reallocはあとで確保領域を縮小する感じで使った方がよいかもしれません。
フラグメント化しないように工夫はされているようですが細かく確保を繰り返すと
あまり効率はよくないです。
    • good
    • 0

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~ …
http://www.jp.freebsd.org/cgi/mroff.cgi?sect=3&s …

ファイルを読みながらデータを保存する容量を増やし、配列を使う場合、プログラムはこんなかんじになるでしょう。(コンパイルしてみてないのでコンパイルできるかすら保証しませんが)
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を使うと多少は楽かもしれません。

この回答への補足

ご回答ありがとうございます.
丁寧な回答で、とても勉強になりました.
ちなみに使用している言語はC++で,質問タイトルは間違ってます.すみませんでした.

C++の場合ですとvectorを用いた以下のような記述がベターになるでしょうか?
---------------------------------------------------
vector<Parameter> data_value; // 動的配列
while(fscanf(fp,"%s",&data_name)!=EOF){
data_value.push_back(getParameter( data_name ));
}
---------------------------------------------------

補足日時:2012/09/16 12:47
    • good
    • 0

C言語がまるで,動的に配列が確保できないかのような回答もあるので


一応言っておくと,参考URLのようにできるので,

EOFにならない限り,動的に構造体のメモリ領域を生成していけば良いのでは.

参考URL:http://itpro.nikkeibp.co.jp/article/COLUMN/20061 …
    • good
    • 0

動的に配列を確保できるプログラミング言語があるのですが、それはもう少し複雑な処理をしているようですが、基本的にはNo.1で書かれているようにlist構造を使っているようです。



プログラムの簡単さからいうと、質問者さんが書かれているように最初に行数を数えて、配列要素を確保してからの方が良いように思います。

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

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

    • good
    • 0
この回答へのお礼

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

お礼日時:2012/09/28 18:19

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています


このQ&Aを見た人がよく見るQ&A