• 締切済み

文字列を比較

文字列を比較して 一つでも一致したらファイルに書き込まず抜ける 一つも一致しなかった場合一度書き込んで抜けるという操作を 下記のコードで実現したいんですけど、 "cPid[n] = cStoRegiAdd; for(j = 0;j < MAX;j++){ if(cPid[n] != cRegiTab[j]){ iflag = 1; } } if(iflag == 1){ fprintf(fh,"#define %s 0\n",cStoRegiAdd); }" の部分で何かおかしいとこはありますか?? もしあれば教えていただきたいんですけど?? #include<stdio.h> #include<string.h> #include<stdlib.h> #define MAX 1024 char *cRegiTab[MAX]; // 文字列登録表 int n = 0; int main() { rightsidehfunction(); leftsidehfunction(); } /* 左辺の値をとって.hファイルに書き込む関数 */ int leftsidehfunction() { FILE *ft; FILE *fh; char buf[MAX]; char buf1[MAX]; char *cPid[MAX]; char *cWorkRegiAdd; char *cStoRegiAdd; int istr = 0; int k; char *p; char *q; int s; int i; int j; int iflag = 0; 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; for(j = 0;j < MAX;j++){ if(cPid[n] != cRegiTab[j]){ iflag = 1; } } if(iflag == 1){ fprintf(fh,"#define %s 0\n",cStoRegiAdd); } n++; } 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; // 文字列格納域アドレス 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)
回答No.11

> もしMAXポイント数を超えた場合エラー これはトークンの総数がMAXを超えた場合、ということでいいですか? でしたら、cRegiTab[n]に登録する前にn < MAXでなければエラー、としてください。

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.10

んー…… ではまがりなりにもコードを書いた私からも#8と似たような事を言いましょうか。 とりあえず#7のコードを見れば、あなたのコードがカケラもないのはわかりますよね。 これは以前のQ/Aとか提示コードから期待される動作を推測したうえで完全に書き直しており、「設計からやり直したほうが早い」方のコードです。 まぁトークン切り出し部分が間違ってるかもしれませんが、そこは仕様を推測して書いてるため&切り出し部のコードは読み込んでないって事でご容赦を。 #トークンを配列のままリスト構造にしてないのはわざとです。念のため で、コードを書く際に気をつけることは…… ・手順を明確にし、切り分けられるところは切り分けて複数のことを同時にやろうとしない  たとえばリスト作成とファイル出力を#7では分けてます。この程度なら同時にやってしまってもさほど問題はないんですが、分けることにより出力ファイルの管理をここでやらなくてもよくなっています。  一箇所で管理するものはなるべく少なくすることで、見通しがよくデバッグのしやすいプログラムになります。 ・例示されたコードは少なくとも「なぜそうなるのか」が理解できるまで絶対に使わない  これは動作が理解できていないと「自分の環境への適応」や「不具合があったときの切り分け」ができないため。  #5のコードはトリッキーな部類なので今は見なかったことにしておいた方がいいかもしれません。 ・わかりやすい変数名・関数名を入れる。適度に何をしているかのコメントもつける  特に複数の英単語で構成されるような名前は全部小文字で区切りなしだと何書いてるのかわからなくなります。_で区切るか2単語目以降の頭文字を大文字にするとわかりやすくなります。  また「頭文字だけ取ってあわせた略称」はやめた方がいいです。一般的なものならともかく、そうでない場合変数名から意味を推察できなくなります。  コメントは将来の自分用と思って、少なくとも処理内容単位で何やってるかを書いておいた方がいいですね。 ・変数の役割、スコープ、入出力のタイミングを把握する  これは最初の「手順を明確に」にも通じるところですが、「(トークン数カウンタの)nをグローバル変数に」の理由が理解できていなかったようなので。 キツいことを言われてカチンとくるのはわからなくもないですが、私も概ね#8と同じ意見であると思ってもらっていいです。 ここでは仕事じゃないんで「その場で動けばとりあえずいいか」な回答もした上で、こういった原則を述べるようにはしてますが。 #なお今暇人してるのは別にサボってる訳じゃなくて入院中だからです。

lilhalileh
質問者

補足

ご返信ありがとうございます。いろいろと勉強になりました。 修行不足だったみたいですんでまたいちから出直してきます。 最後に一つだけ質問させてください。本当にこれが最後ですので☆ 聞きながらも結構苦労してコードを完成させたので.... 最初のコードで#define MAX 1024と定義しているのですが。。。。 もしMAXポイント数を超えた場合エラーという出力をさせる場合 どういう感じにしたらいいでしょうか?? どこでifを使ってエラーを出力させるかがいまいちわかりません。 最初に自分が書いたコードでいいのでよろしくお願いできますでしょうか?? とりあえずこれが最期なので一生のおねがいということで☆

  • asuncion
  • ベストアンサー率33% (2127/6289)
回答No.9

ご自分の頭をフル回転させて、ウンウンうなりながら 作ったプログラムが動いたときの気持ちよさを味わえるようになってください。 人からもらったプログラムをただ動かすだけでは、プログラミングという 知的作業の醍醐味を感じることができません。

  • arain
  • ベストアンサー率27% (292/1049)
回答No.8

No.2です。 改めて書きましょう。 >結論から言うと、設計からやり直した方が早いです。 >今まで質問した問題箇所「だけ」の回答をツギハギしているので、 >かえってややこしくて、わかりにくく、追加変更が困難なものができています。 さらに追加された理由。 No.4より >なのでこの5つがグローバル変数(cRegiTab[n])で宣言している配列に入りますよね?? >今は試験的に行っているので実際は何行あるか分からないので10行かもしれないし....100行という可能性もある。 可変であれば、動的にメモリ確保し制御できるように「リスト構造」を使用する形で再設計してからプログラムを組むことを考えてください。 今の状態では当然ながらMAX間までしか処理できません。 机上で設計もせずに「頭の中だけで」(言いかえれば「行き当たりばったりでいきなりソースを組む」)ことを行っていませんか? 自分で提示したソースの ・どこで何を行っているか ・それぞれの変数にどのようなタイミングでどんな値が入っているか ご自分で理解できていますか? 少なくとも、 ・わかりやすい変数名を使用する ・処理にコメントを記載していく 等を行えば、プログラムの問題点も把握しやすい見通しの良いソースになりますよ。 回答者がどれだけ短い処理で同じことを行っているかを考えれば理解できるでしょう。 No.5より >セミコロンだけの行があるんですけど、あれはどういう意味でしょうか?? 空行。No.5の場合ならforループだけを実行させるために使用されている。 >それと自分が作ったやつにあなたの作成したコードを組み込めば動く!?ということでしょうか?? ソースの内容を理解せずに組み込んだら、「また」今までと同じことを繰り返すことになりますよ。

lilhalileh
質問者

補足

そんなに簡単におっしゃるならコードを 提示していただけませんか?? 口だけで技術が伴っていない人のアドバイスを 聞いてもこちらはなんの意味もないですし。。 それだけ自身がおありならできますよね??

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.7

む、テキストファイルとか添付できないのか…… 仕方ないのでここに直接出しておきます。 可読性のためインデントに全角スペースを使ってます。 #include <stdio.h> #include <stdlib.h> #include <string.h> /* 一行の最大長 */ #define LINE_LEN 256 /* トークンの最大長 */ #define TOKEN_LEN 32 /* トークンの最大数 */ #define TOKEN_MAX 256 int get_token_list(FILE *infile, char token_list[TOKEN_MAX][TOKEN_LEN]); void write_header(FILE *outfile, char token_list[TOKEN_MAX][TOKEN_LEN], int token_num); int main() {  FILE *fp;  char token_list[TOKEN_MAX][TOKEN_LEN];  int token_num;  fp = fopen("test.txt","r");  if(!fp) return -1;  token_num = get_token_list(fp, token_list);  fclose(fp);    fp = fopen("test.h", "w");  if(!fp) return -1;  write_header(fp, token_list, token_num);  fclose(fp);  return 0; } /* 入力ファイルからトークンのリストを作成(戻り値:トークンリストのエントリー数) */ int get_token_list(FILE *infile, char token_list[TOKEN_MAX][TOKEN_LEN]) {  char line[LINE_LEN];  char token[TOKEN_LEN];  int token_num = 0;  int i;  char *ptr;  int len;  while(fgets(line, LINE_LEN, infile))  {   ptr = strchr(line, '=');   while(ptr != NULL)   {    /* 右辺の処理 */    if(ptr != NULL)    {     /* ダブルクォート(開き)を検索 */     ptr = strchr(ptr, '\"');     if(ptr != NULL)     {      /* ダブルクォートの隣にポインタを移動 */      ptr ++;      /* ダブルクォート(閉じ)までの長さを取得 */      len = strcspn(ptr, "\"");            strncpy(token, ptr, len);      token[len] = '\0';            /* ダブルクォート(閉じ)の後ろまでずらす */      ptr += len + 2;     }    }        /* 左辺の処理(右辺に処理する相手がいなくなった時点でptr==NULLになり動作する) */    /* strtok()でlineを破壊するので右辺のあとでの実行 */    if(ptr == NULL)    {     /* 左辺の場合 */     ptr = strtok(line, "=");     strcpy(token, ptr);          /* この処理が終わったらループアウトするので条件を設定しておく */     ptr = NULL;    }    /* 重複チェック */    for(i = 0; i < token_num; i ++){     /* 重複トークンがあったらとめる */     if(strcmp(token, token_list[i]) == 0){      break;     }    }    /* i == token_numなら重複がなかったことになるので新要素として追加 */    if(i == token_num){     strcpy(token_list[token_num], token);     token_num ++;    }   }  }  return token_num; } /* トークンリストをヘッダファイルに書き出し */ void write_header(FILE *outfile, char token_list[TOKEN_MAX][TOKEN_LEN], int token_num) {  int i;  for(i = 0; i < token_num; i ++)  {   fprintf(outfile, "#define %s 0\n", token_list[i]);  } }

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.6

つまり「右辺処理後のトークン総数が処理前にわからない」ということですか。それならわからなくても問題ありません。 そこを簡単に左辺処理へ引き継ぐために前回のQ/Aで「nをグローバル変数にする」と言ったのですから。 nは右辺処理中にトークンが追加されるたびに加算される、つまり右辺処理終了後には「右辺の重複しないトークン総数」が入っています。 nはグローバル変数なのでどの関数でも読み書きできますから、何も考えずにそのままnを使えばいいわけです。 あと暇に飽かせてコードを書いてみました。 POINT_A="POINT_B"+"POINT_C" POINT_B="POINT_C"*"POINT_A" KEN=("MMK2","MSX") KOL="HIMWARI"+1000000"OKL" から #define POINT_B 0 #define POINT_C 0 #define POINT_A 0 #define MMK2 0 #define MSX 0 #define KEN 0 #define HIMWARI 0 #define OKL 0 #define KOL 0 が生成されます。 若干トリッキーなことをしていたり異常構文(ダブルクォートの対応がとれてないとか""(空トークン)の処理とか)の対応はしてませんが、まぁ参考までに。

lilhalileh
質問者

補足

ありがとうございます。 え......と。 コードはいずこに(笑) 参考にさせていただこうと思ったんですけど.... ファイルの中身と結果はあるんですけど。。。。。。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

わざわざ fputc を使うから面倒なのであって, 諦めて fprintf を使えば簡単なのでは? void printSymbols(char *buf, FILE *fout) { char *p = buf; char *q; /* Left hand side mode */ for (q = p; *q != '\0' && *q != '='; ++q) { ; } fprintf(fout, "#define %.*s 0\n", (int)(q-p), p); /* Right hand side mode */ while (*q != '\0') { for (p = q+1; *p != '\0' && *p != '"'; ++p) { ; } if (*p == '\0') { return; } for (q = ++p; *q != '\0' && *q != '"'; ++q) { ; } fprintf(fout, "#define %.*s 0\n", (int)(q-p), p); } でそれなりな動作になるかな?

lilhalileh
質問者

補足

ありがとうございます。 セミコロンだけの行があるんですけど、あれはどういう意味でしょうか?? それと自分が作ったやつにあなたの作成したコードを組み込めば動く!?ということでしょうか??

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.4

> ファイルの読み込み数によって違う 「ファイルの読み込み数によって違う」という言葉の意味がよくわからないのですが…… 複数ファイルがターゲットで、ファイルごとにnの値が変わるという意味でしょうか。 もうちょっと具体的にお願いします。 で、#2の補足に書いてあるコードですけど、これだと書式出力だけでなく重複チェックもできませんね。 一文字ずつ書くのではなく、いったん文字列に移し変えるようにしましょう。 あと、文字列関数にはこの手の処理に使える便利な関数がいくつか存在しますので、それを使って処理を書き換えてしまいましょう。 フラグやらループやらいろいろと消えて見通しがよくなります。 以下は「文字列bufの=より右にある""で囲まれた最初のトークンを切り出す」処理のサンプルです。 不明な関数はヘルプで調べてください。あとエラーチェックは端折ってます。 #define TOKEN_LEN 32 char token[TOKEN_LEN]; char *ptr; int token_len; /* =までポインタを移動 */ ptr = strchr(buf, '='); /* =より後の最初のダブルクォート(開き)を検索 */ ptr = strchr(ptr, '\"'); /* ダブルクォートはコピー対象に含まないので一つ隣に移動 */ ptr ++; /* ダブルクォート(閉じ)までの長さを取得 */ token_len = strcspn(ptr, "\""); /* tokenにptrからtoken_lenだけコピー */ strncpy(token, ptr, token_len); token[token_len] = '\0';

lilhalileh
質問者

補足

前に説明したんですけどファイルを読んで、.hファイルに書きこむん ですけどファイルの中身が AE="AO"+"AC" AE="AC"+"AE" AW="AD"*"AG" だとします。 今の僕が作ったやつでいくとまず右辺の値をとって 次に左辺の値をとります。このままファイルを読み込んで.hファイルに出力させると AO AC AE AD AG AE AW ですよね。 書き込むファイルの内容は重複しちゃいけないんで AO AC AE AD AG AW としたいわけですよ。 ここまではいいですよね?? 右辺と左辺を分けてるからまず右辺のクオート括りの文字を抽出。 その場合ファイルに書き込まれている文字は AO AC AE AD AG なのでこの5つがグローバル変数(cRegiTab[n])で宣言している配列に入りますよね??今は試験的に行っているので実際は何行あるか分からないので10行かもしれないし....100行という可能性もある。 それで右辺の切り取った文字の配列に入っている個数を 正確に調べるにはどうしたらいいかという質問です。

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.3

とりあえず#1ですが、それ以外にも。 > cPid[n] = cStoRegiAdd; > for(j = 0;j < MAX;j++){ > if(cPid[n] != cRegiTab[j]){ > iflag = 1; > } > } forループ脱出条件はj < n(cRegiTabへの登録数まで)にしましょう。でないと文字列の入っていない要素まで到達した瞬間にコケます。 あと、これではループのどこかで一回一致しなかったらその時点でiflagが立ったままになります(あとで一致しても一致したとみなされない)。 ループの外で1にセットしておき、同値が出たら0にセットしてbreakしましょう。 てなわけで、変更するならこんな感じになりますか。 iflag = 1; for(j = 0;j < n;j++){ if(strcmp(cPid[n],cRegiTab[j]) == 0){ iflag = 0; break; } }

lilhalileh
質問者

補足

ご返信どうもありがとうございます。 j < n(cRegiTabへの登録数)ですが ファイルの読み込み数によって違うんですけど どういう風に指定したらいいでしょうか??

  • arain
  • ベストアンサー率27% (292/1049)
回答No.2

前回の質問 http://okwave.jp/qa4506588.html の指摘箇所があまり反映されていないのは「ここでは」置いておくとして >下記のコードで実現したいんですけど、 下記のコードでは実現できません。 >の部分で何かおかしいとこはありますか?? あります。 で「(上記で)置いておいた」ことを蒸し返します。 結論から言うと、設計からやり直した方が早いです。 今まで質問した問題箇所「だけ」の回答をツギハギしているので、 かえってややこしくて、わかりにくく、追加変更が困難なものができています。 一応今回の問題箇所だけは指摘しときますが >if(cPid[n] != cRegiTab[j]){ 「文字列」はif文の比較演算子での比較はできません。

lilhalileh
質問者

補足

ご返信どうもです。 別のパターンは作っています。 ただ一つ書式付で出力できないんですよ。。。。。 そこを教えていただきたく思います。下にソースを載せます。 fputcの部分をどうすればいいでしょうか?? よろしくお願いします。 #include <stdio.h> #define MAX 256 int main() { FILE *in, *out; char *p, buf[MAX]; int lhs, in_quote; in = fopen("sample.txt", "r"); if (in == NULL) { printf("Cannot open sample.txt\n"); return 1; } out = fopen("sample.h", "w"); if (out == NULL) { printf("Cannot open sample.h\n"); return 1; } while (fgets(buf, MAX, in) != NULL) { lhs = 1; in_quote = 0; for (p = buf; *p != '\0'; p++) if (*p == '=') { lhs = 0; fputc('\n', out); } else if (*p == '"') if (in_quote) { in_quote = 0; fputc('\n', out); } else in_quote = 1; else if (lhs || in_quote) fputc(*p, out); } fclose(out); fclose(in); }

関連するQ&A