- 締切済み
whileは2度呼べる!?
質問させていただきます。。 何度か似たような質問をさせて頂いていますが、 今回もちょっと掘り下げた質問になっていますので。。。。 ファイルの内容を読み取って.hファイルに書き込むのですが。 ファイルの中身が POINT_A="POINT_B"+"POINT_C" POINT_B="POINT_C"*"POINT_A" だとします。 狙いとしては POINT_A POINT_B POINT_C です。下記のソースでは右辺と左辺を分けているのですが.... 重複した場合.hファイルに書き込まないようにしています。 問題の場所は/* 右辺の値をとって.hファイルに書き込む関数 */の部分で2回while を使って読み込みをしているのですが何かおかしいとこは ありますでしょうか?? #include<stdio.h> #include<string.h> #include<stdlib.h> #define MAX 10000 int leftsidehfunction(); int rightsidehfunction(); int main(int argc ,char **argv) { if(argc != 2 ){ printf("引数の数があっていません。\n"); } rightsidehfunction(argv[1]); } /* 右辺の値をとって.hファイルに書き込む関数 */ int rightsidehfunction(char cfilename[MAX]) { FILE *ft; // 入力ファイルポインタ FILE *fh; // 出力ファイルポインタ int c; //読み込み文字 char buf[MAX]; //バッファ領域 char buf1[MAX]; char cPid[MAX]; // 採取したPID char *cWorkRegiAdd; // 採取文字列作業域アドレス char *cStoRegiAdd; // 文字列格納域アドレス char *cRegiTab[MAX]; // 文字列登録表 int iflag = 0; // 0/1; 右/左のクオート int ichs = 0; // chrの文字数 int istr = 0; // strの文字数 int k; // ntrの探索index int iOid; //OID char cgetfilename[MAX]; char *p; strcpy(cgetfilename,cfilename); ft = fopen(cgetfilename, "r"); strtok(cgetfilename,"."); strcat(cgetfilename,".h"); fh = fopen(cgetfilename, "a+"); if (ft == NULL || fh == NULL) { printf("%s ファイルを開けません。\n",cgetfilename); } while ((c = fgetc(ft)) != EOF) { switch (c) { case '"': iflag = 1 - iflag; /*右のクオート*/ while(fgets(buf1,sizeof buf1,fh) != NULL){ strtok(buf1," "); p = strtok(NULL," "); if (iflag == 0) { cPid[ichs] = '\0'; cWorkRegiAdd = cPid; for(k = 0;k < istr;k++){ if(*cRegiTab[k] != *cWorkRegiAdd) continue; if(strcmp(cRegiTab[k],cWorkRegiAdd) == 0) break; } if(k == istr){ //no hit in str[] cStoRegiAdd = malloc(strlen(cWorkRegiAdd) + 1); strcpy(cStoRegiAdd,cWorkRegiAdd); cRegiTab[istr] = cStoRegiAdd; istr++; db_obj_find_pid(cStoRegiAdd, &iOid); if(iOid <= 0 ){ if(p != cStoRegiAdd){ fprintf(fh, "#define %s 0\n", cStoRegiAdd); }else if(p == cStoRegiAdd){ break; } }else{ if(p != cStoRegiAdd){ fprintf(fh, "#define %s output[obj[%d].outputs + 2].Analog\n",cStoRegiAdd, iOid); }else if(p == cStoRegiAdd){ break; } } } ichs = 0; } iOid = 0; } break; case '\n': if (iflag == 1) { cPid[ichs] = '\0'; iflag = 0; } ichs = 0; break; default: if (iflag == 1) { cPid[ichs] = c; if (ichs < MAX - 1) { ichs++; } } break; } } if (ichs>0) { cPid[ichs]='\0'; } fclose(ft); fclose(fh); return 0; }
- みんなの回答 (9)
- 専門家の回答
みんなの回答
こんにちは。 プログラムを修正するよりプログラムの設計変更をした方がいいです。 “うまくいったので”という理由ならなおさらです。 もっと楽に実現できる方法を考えるようにして下さい。 おそらく与えられた問題をそのままの形でプログラムを書こうとして いると思われますので仕様の追加や変更があった場合に使い物にならなく なります。 例えば以下のような場合です。 “拾ったキーワードを昇順または降順に並び替えて” “拾ったキーワードの中で先頭に特定の文字を含むのだけ出して” #8さんの回答でプログラムを設計すると上記の仕様追加もシンボル テーブルの操作で実現できますし、あらかじめ仕様追加に対応した プログラムが作成できます。 ご参考までに。
- Tacosan
- ベストアンサー率23% (3656/15482)
#6 を簡単に言うと 「こんな面倒なことをしている理由が分からない」 ではないかな. この見解には私も賛成です. はっきりいって 1. 当該出力ファイルから読み込んでシンボルテーブルを作る 2. 入力ファイルから式を読み込んでシンボルテーブルを更新する 3. シンボルテーブルを参照しながら改めて出力ファイルに書きだす という処理の方が明らかに簡単だと思うんだけど, こんな面倒な処理をしようとしている理由は何でしょうか? ちなみに, 出力の .h ファイルで重複してはいけないとする理由を教えていただけると嬉しい.
- D-Matsu
- ベストアンサー率45% (1080/2394)
いや、ですから#5で > 元コードを生かす形なら > cRegiTabとnをグローバル変数化する って書いてるんですけど。動かしてみた訳じゃないですが、今のコードでは確実に右辺切り出し処理の結果は左辺切り出し時にチェックされません。 単純にこれを解消するならグローバル変数を使うのが楽です。 あまり推奨できる手段でもありませんが、一番簡単な解法ではあります。 以下余談な訳ですが、 > 書式は決まっていない "左辺=右辺"までは書式が決まってますよね?右辺自身の書式はたしかに変動してるようですけど、それを処理するコードは既に書かれていますし。 なので、取得した行に対して左辺処理と右辺処理を一度に行う、というやり方がある訳です。 これならグローバル変数を使うまでもなく一関数内で処理ができます。
- chie65536
- ベストアンサー率41% (2512/6032)
こういう処理は、普通は、トークンが現れる度に、メモリ上の登録リストを見て、リストにトークンが無ければリストに登録、リストにあれば無視、と言うのを繰り返し、最後にリストを出力する、と言う方法を取る。 で「リストに登録済みかどうか?」のチェックに、ハッシュを使うとか、ライブラリにあるリストオブジェクトを重複無しモードで使うとか、ちょっとした工夫をする。 「ファイルに出力済みのデータと重複してるかどうか調べる」と言うのは、使用可能なメモリが極端に少ないとか、ファイルアクセスがメモリアクセスよりも高速だとか、かなり特殊な環境ででしか行わない。 普通、仕事でそういうソースコードを記述したら、上司に後頭部を殴られる。
補足
あなたの見解は分かりました。。。。 とどのつまり出来るんでしょうか?? 出来ないんでしょうか?? かなり特殊な環境じゃ使われないでは分からないんですど。。
- D-Matsu
- ベストアンサー率45% (1080/2394)
複数の補足にまったく同じもの(コードも含めて)を載せるのは正直うっとうしいです。特にコードは一箇所で提示してくれれば十分。 で本題ですが、元コードをなるべく生かす形でやるなら cRegiTabとnをグローバル変数化する のがたぶん楽です。このコードでは左辺処理と右辺処理で処理済文字列が別のローカル変数であるため左辺と右辺の重複チェックが働かないので。 が、自分で組むとしたら左辺・右辺で関数を分けずに一行ごとに一括処理する方向で作りますね。 書式は決まってるようなのでさほど面倒ではないと思いますが。
補足
え....と 書式は決まっていないですよ。 例えば KEN=("MMK2","MSX") とか KOL="HIMWARI"+1000000"OKL"とか 不規則なので.....。いろいろ試行錯誤して左辺と右辺を 分けるというやり方を選びました。 つまり私の考える案ではできないということでしょうか?? 他の可能性とかじゃなく出来るかどうかをお聞きしているんですが。。。。。。。
- arain
- ベストアンサー率27% (292/1049)
とりあえず別方面から ・コンパイルが通っているソースを提示すること。 これは何度も指摘しているので説明は省く ・「意図する結果」と「実行した結果」を明示すること。 回答者はソースを解析して実行結果を「推測」するしかない。非常に時間の無駄が生じる。 ・fopen()失敗時、開くことができたファイルはクローズしてから終了すること。 ・malloc()に対応したfree()が存在しない。 システム系によっては終了しても確保された領域がそのままゴミとして存在したままになることがある。 ・if-elseやseitch-caee、for/while文には「何を行っているか(なんの条件か)」は明記する。 解析の手間にもなるし、後で自分で火ソースを見直す時に泣きを見ることになる。 あと、auto変数で5000Byte(MAX:10000 * 配列五つ)ってのも気になる。 「スタックオーバーフロー」起こす元になるよ。 本当にそれだけの領域が必要なのかな?
補足
アドバイスは真摯に受け止めて今後留意していきたいと思います。 で本題ですが動くものを載せました。テキストの内容が KO="AB"+"AC" AB="KO"+"AC" の場合 結果は #define KO 0 #define AB 0 #define AC 0 #define KO 0 #define AB 0 となります これを重複したやつをなくして #define KO 0 #define AB 0 #define AC 0 このような結果にするにはどうしたらいいでしょうか?? #include<stdio.h> #include<string.h> #include<stdlib.h> #define MAX 1024 int main() { rightsidehfunction(); leftsidehfunction(); } /* 左辺の値をとって.hファイルに書き込む関数 */ int leftsidehfunction() { FILE *ft; FILE *fh; char buf[MAX]; char *cPid[MAX]; char *cWorkRegiAdd; char *cStoRegiAdd; int n = 0; int istr = 0; int k; char *p; ft = fopen("test.txt","r"); fh = fopen("test.h","a+"); if (fh == NULL || ft == NULL) { printf("ファイルを開けません。\n"); return 1; } /* ファイルを1行ずつ読み込む処理 */ while (fgets(buf, sizeof buf, ft) != NULL){ cWorkRegiAdd = strtok(buf, "="); for(k = 0;k < n;k++){ if(*cPid[k] != *cWorkRegiAdd)continue; if(strcmp(cPid[k],cWorkRegiAdd) == 0)break; } if(k < n) continue; cStoRegiAdd = malloc(strlen(cWorkRegiAdd) + 1); strcpy(cStoRegiAdd,cWorkRegiAdd); cPid[n] = cStoRegiAdd; n++; fprintf(fh, "#define %s 0\n", cStoRegiAdd); } fclose(fh); fclose(ft); return 0; } /* 右辺の値をとって.hファイルに書き込む関数 */ int rightsidehfunction() { FILE *ft; // 入力ファイルポインタ FILE *fh; // 出力ファイルポインタ int c; //読み込み文字 char buf[MAX]; //バッファ領域 char buf1[MAX]; char cPid[MAX]; // 採取したPID char *cWorkRegiAdd; // 採取文字列作業域アドレス char *cStoRegiAdd; // 文字列格納域アドレス char *cRegiTab[MAX]; // 文字列登録表 int iflag = 0; // 0/1; 右/左のクオート int ichs = 0; // chrの文字数 int istr = 0; // strの文字数 int k; // ntrの探索index int iOid; //OID char *p; ft = fopen("test.txt", "r"); fh = fopen("test.h", "w"); if (fh == NULL || ft == NULL) { printf("ファイルを開けません。\n"); return 1; } //ファイルを1文字ずつ読み込む while ((c = fgetc(ft)) != EOF) { switch (c) { case '"': iflag = 1 - iflag; /*右のクオート*/ if (iflag == 0) { cPid[ichs] = '\0'; cWorkRegiAdd = cPid; for(k = 0;k < istr;k++){ if(*cRegiTab[k] != *cWorkRegiAdd) continue; if(strcmp(cRegiTab[k],cWorkRegiAdd) == 0) break; } if(k == istr){ cStoRegiAdd = malloc(strlen(cWorkRegiAdd) + 1); strcpy(cStoRegiAdd,cWorkRegiAdd); cRegiTab[istr] = cStoRegiAdd; istr++; fprintf(fh, "#define %s 0\n", cStoRegiAdd); } ichs = 0; } break; case '\n': if (iflag == 1) { cPid[ichs] = '\0'; iflag = 0; } ichs = 0; break; default: if (iflag == 1) { cPid[ichs] = c; if (ichs < MAX - 1) { ichs++; } } break; } } /* ファイルの最後が改行じゃなかった場合 */ if (ichs>0) { cPid[ichs]='\0'; } fclose(ft); fclose(fh); return 0; }
- D-Matsu
- ベストアンサー率45% (1080/2394)
「コンパイルエラー以外の質問なら、ちゃんとコンパイル可能なソースを書きましょう」ってのは他の方からも言われている通りです。 それはさておき、ループは条件の書き方次第で何重にでもできます。まぁそんなループは推奨される書き方じゃないですが。 何がどううまくいかないのか、が書いてないですが、まずいのはたぶんここ。 > while(fgets(buf1,sizeof buf1,fh) != NULL){ 「書き込み済みファイルの中身を使った既出チェック」をやっているようなのに、ループ前にfhをファイル先頭に戻してないのでファイル終端から読み始めてしまい、絶対にループインしない。 #かつチェック後にファイル終端に戻さないと悲惨なことに どうやるのかはfseek()を調べてください。
補足
アドバイスは真摯に受け止めて今後留意していきたいと思います。 で本題ですが動くものを載せました。テキストの内容が KO="AB"+"AC" AB="KO"+"AC" の場合 結果は #define KO 0 #define AB 0 #define AC 0 #define KO 0 #define AB 0 となります これを重複したやつをなくして #define KO 0 #define AB 0 #define AC 0 このような結果にするにはどうしたらいいでしょうか?? #include<stdio.h> #include<string.h> #include<stdlib.h> #define MAX 1024 int main() { rightsidehfunction(); leftsidehfunction(); } /* 左辺の値をとって.hファイルに書き込む関数 */ int leftsidehfunction() { FILE *ft; FILE *fh; char buf[MAX]; char *cPid[MAX]; char *cWorkRegiAdd; char *cStoRegiAdd; int n = 0; int istr = 0; int k; char *p; ft = fopen("test.txt","r"); fh = fopen("test.h","a+"); if (fh == NULL || ft == NULL) { printf("ファイルを開けません。\n"); return 1; } /* ファイルを1行ずつ読み込む処理 */ while (fgets(buf, sizeof buf, ft) != NULL){ cWorkRegiAdd = strtok(buf, "="); for(k = 0;k < n;k++){ if(*cPid[k] != *cWorkRegiAdd)continue; if(strcmp(cPid[k],cWorkRegiAdd) == 0)break; } if(k < n) continue; cStoRegiAdd = malloc(strlen(cWorkRegiAdd) + 1); strcpy(cStoRegiAdd,cWorkRegiAdd); cPid[n] = cStoRegiAdd; n++; fprintf(fh, "#define %s 0\n", cStoRegiAdd); } fclose(fh); fclose(ft); return 0; } /* 右辺の値をとって.hファイルに書き込む関数 */ int rightsidehfunction() { FILE *ft; // 入力ファイルポインタ FILE *fh; // 出力ファイルポインタ int c; //読み込み文字 char buf[MAX]; //バッファ領域 char buf1[MAX]; char cPid[MAX]; // 採取したPID char *cWorkRegiAdd; // 採取文字列作業域アドレス char *cStoRegiAdd; // 文字列格納域アドレス char *cRegiTab[MAX]; // 文字列登録表 int iflag = 0; // 0/1; 右/左のクオート int ichs = 0; // chrの文字数 int istr = 0; // strの文字数 int k; // ntrの探索index int iOid; //OID char *p; ft = fopen("test.txt", "r"); fh = fopen("test.h", "w"); if (fh == NULL || ft == NULL) { printf("ファイルを開けません。\n"); return 1; } //ファイルを1文字ずつ読み込む while ((c = fgetc(ft)) != EOF) { switch (c) { case '"': iflag = 1 - iflag; /*右のクオート*/ if (iflag == 0) { cPid[ichs] = '\0'; cWorkRegiAdd = cPid; for(k = 0;k < istr;k++){ if(*cRegiTab[k] != *cWorkRegiAdd) continue; if(strcmp(cRegiTab[k],cWorkRegiAdd) == 0) break; } if(k == istr){ cStoRegiAdd = malloc(strlen(cWorkRegiAdd) + 1); strcpy(cStoRegiAdd,cWorkRegiAdd); cRegiTab[istr] = cStoRegiAdd; istr++; fprintf(fh, "#define %s 0\n", cStoRegiAdd); } ichs = 0; } break; case '\n': if (iflag == 1) { cPid[ichs] = '\0'; iflag = 0; } ichs = 0; break; default: if (iflag == 1) { cPid[ichs] = c; if (ichs < MAX - 1) { ichs++; } } break; } } /* ファイルの最後が改行じゃなかった場合 */ if (ichs>0) { cPid[ichs]='\0'; } fclose(ft); fclose(fh); return 0; }
- Tacosan
- ベストアンサー率23% (3656/15482)
うう, インデントが崩れると全く読めない. さておき, わざわざ while をネストする必要性って何だろう. ちょっとは速くなるのかなぁ? あと表面的に眺めた限りでは途中の if 文, if(p != cStoRegiAdd){ は必ずこの条件が成り立つような気がするのですが, それは意図したとおりでしょうか?
補足
アドバイスは真摯に受け止めて今後留意していきたいと思います。 で本題ですが動くものを載せました。テキストの内容が KO="AB"+"AC" AB="KO"+"AC" の場合 結果は #define KO 0 #define AB 0 #define AC 0 #define KO 0 #define AB 0 となります これを重複したやつをなくして #define KO 0 #define AB 0 #define AC 0 このような結果にするにはどうしたらいいでしょうか?? #include<stdio.h> #include<string.h> #include<stdlib.h> #define MAX 1024 int main() { rightsidehfunction(); leftsidehfunction(); } /* 左辺の値をとって.hファイルに書き込む関数 */ int leftsidehfunction() { FILE *ft; FILE *fh; char buf[MAX]; char *cPid[MAX]; char *cWorkRegiAdd; char *cStoRegiAdd; int n = 0; int istr = 0; int k; char *p; ft = fopen("test.txt","r"); fh = fopen("test.h","a+"); if (fh == NULL || ft == NULL) { printf("ファイルを開けません。\n"); return 1; } /* ファイルを1行ずつ読み込む処理 */ while (fgets(buf, sizeof buf, ft) != NULL){ cWorkRegiAdd = strtok(buf, "="); for(k = 0;k < n;k++){ if(*cPid[k] != *cWorkRegiAdd)continue; if(strcmp(cPid[k],cWorkRegiAdd) == 0)break; } if(k < n) continue; cStoRegiAdd = malloc(strlen(cWorkRegiAdd) + 1); strcpy(cStoRegiAdd,cWorkRegiAdd); cPid[n] = cStoRegiAdd; n++; fprintf(fh, "#define %s 0\n", cStoRegiAdd); } fclose(fh); fclose(ft); return 0; } /* 右辺の値をとって.hファイルに書き込む関数 */ int rightsidehfunction() { FILE *ft; // 入力ファイルポインタ FILE *fh; // 出力ファイルポインタ int c; //読み込み文字 char buf[MAX]; //バッファ領域 char buf1[MAX]; char cPid[MAX]; // 採取したPID char *cWorkRegiAdd; // 採取文字列作業域アドレス char *cStoRegiAdd; // 文字列格納域アドレス char *cRegiTab[MAX]; // 文字列登録表 int iflag = 0; // 0/1; 右/左のクオート int ichs = 0; // chrの文字数 int istr = 0; // strの文字数 int k; // ntrの探索index int iOid; //OID char *p; ft = fopen("test.txt", "r"); fh = fopen("test.h", "w"); if (fh == NULL || ft == NULL) { printf("ファイルを開けません。\n"); return 1; } //ファイルを1文字ずつ読み込む while ((c = fgetc(ft)) != EOF) { switch (c) { case '"': iflag = 1 - iflag; /*右のクオート*/ if (iflag == 0) { cPid[ichs] = '\0'; cWorkRegiAdd = cPid; for(k = 0;k < istr;k++){ if(*cRegiTab[k] != *cWorkRegiAdd) continue; if(strcmp(cRegiTab[k],cWorkRegiAdd) == 0) break; } if(k == istr){ cStoRegiAdd = malloc(strlen(cWorkRegiAdd) + 1); strcpy(cStoRegiAdd,cWorkRegiAdd); cRegiTab[istr] = cStoRegiAdd; istr++; fprintf(fh, "#define %s 0\n", cStoRegiAdd); } ichs = 0; } break; case '\n': if (iflag == 1) { cPid[ichs] = '\0'; iflag = 0; } ichs = 0; break; default: if (iflag == 1) { cPid[ichs] = c; if (ichs < MAX - 1) { ichs++; } } break; } } /* ファイルの最後が改行じゃなかった場合 */ if (ichs>0) { cPid[ichs]='\0'; } fclose(ft); fclose(fh); return 0; }
- postal0x02
- ベストアンサー率42% (24/57)
コンパイルできません。 >int rightsidehfunction(); >int rightsidehfunction(char cfilename[MAX]) プロトタイプ宣言と実体の定義が違います。 >if (ft == NULL || fh == NULL) { > printf("%s ファイルを開けません。\n",cgetfilename); >} ファイルが無くてもアプリケーションは続行します。 >db_obj_find_pid 未定義とコンパイルエラーが出ます。 db ← 名前からしてデータベース関係のライブラリでしょうか? (検索しても見当たりませんでした) 他にもたくさん(sizeofのカッコ等)ありますが、 致命的なエラーのみをざっと見た感じで出しました。 (まだあると思います)
補足
お返事ありがとうございます。 せっかくやっていただいたんすけど.... 残念ながらコンパイルは通りません。 ちょっと特殊な部分があるんで....... ご指摘いただいた部分に関しては留意します。 whileを2度呼んでるところありますよね?? そこで何か気づく点とかありますでしょうか?? さっきからコンパイルは通るんですけど 実行しても結果がうまくいきませんで。。
補足
今やってるやりがたがしっくりするから。 というよりこれをやったらうまくいったので......。 重複してはいけないのはそういう決まりごと。