• ベストアンサー

構造体を使ったファイルの読み込み

『指定された辞書を配列に読み込み、表記または読みまたは品詞を指定すると、その単語の情報(表記、読み、品詞)を標準出力に出力するプログラムを作成せよ。』 という問題なんですが、 アルゴリズムは 1.単語の個数を数える 2.メモリを確保する 3.辞書を読み込む 4.検索する情報を入力する 5.配列の先頭から順番に、入力された情報が読み、標記にある単語を検索し、出力する。 だと思うんです。 しかし単語数を表示してメモリを確保するところまではできたのですが、辞書の読み込みがうまくいきません。 どこがおかしいのか教えてください。 辞書は 学校,ガッコウ,0010 資格,シカク,0011 のように「表記,読み,品詞」順で カンマで区切られているファイルです。 struct JISYO { char *hyouki,*yomi; int hinsi; }; main(int argc, char *argv[]) { fp = fopen(argv[1],"r"); while(f=fgetc(fp)) { if(f==EOF)break; while(f!=',') { jisyo[count2].hyouki[count3]=f; printf("%d\n",jisyo[count2].hyouki[count3]); count3++; f=fgetc(fp); } jisyo[count2].hyouki[count3]=0; f=fgetc(fp); count3=0; while(f!=',') { f=jisyo[count2].yomi[count3]; count3++; f=fgetc(fp); } jisyo[count2].yomi[count3]=0; count3=0; f=fgetc(fp); fscanf(fp,"%d",&hin); jisyo[count2].hinsi=hin; count2++; } fclose(fp); }

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

  • ベストアンサー
  • rentahero
  • ベストアンサー率53% (182/342)
回答No.5

struct JISYO { char *hyouki,*yomi; int hinsi; }; jisyo=(struct JISYO *)malloc(sizeof(struct JISYO)*count); という構造体の配列の確保は確認しました。 で、肝心のhyoukiとyomiはどこで確保してるんですか。 このままでは、hyoukiとyomiにはゴミが入っていてどこともいえないところのメモリを指しています。間違いなくランタイムエラー(アクセス違反)で落ちます。 したがって、この構造体が問題の方で指定されていたのであれば、一回fread+2回メモリスキャンという形式を想定していたのではないでしょうか。 ということで、擬似コード #include <stdio.h> // printf/fopen/ etc... #include <string.h> // strtok/strcpy/ etc... #include <stdlib.h> // malloc/atoi etc... struct JISYO { char *hyouki; char **yomi; int hinsi; }; main(int argc, char *argv[]) { // 1.辞書ファイルをオープンし、サイズを確認する。 // 1-1.辞書ファイルをバイナリでオープン // 1-2.ファイルの最後にシーク // 1-3.ファイル位置を取得=ファイルサイズ // 2.辞書を全部読み込めるメモリを確保する。*1 // 3.行単位でメモリへ読み込み、単語数をカウントする。 // 3-1.実際には一括読みの方がよい。間違えにくい。 // 4.読み込みが終わったらファイルはさっさとクローズ。 // 3-2.読み込んだメモリをスキャンして(forループ) // 3-3.改行があったらカウントして、'\0'(終端記号)に置き換え。*2 // 5.カウントした行数から、構造体のメモリを確保 // 6.メモリをもう一度スキャンし、以下の処理をする。 // 6-1.辞書のメモリ内から、表記と読みへのポインタを //  struct JISYOにセットしていく。その際、カンマを //  '\0'に置き換えることで文字終端をセットする。 //  行末が'\0'で終わっているわけなので、strtokで分割 //  してもよい。*3 // 6-2.表記と読みへのポインタを取得したら次は品詞が //  文字列で入っているので、数値にする。 // 6-3.くりかえす // 7.検索する情報を読み込む // 8.配列をチェックし、該当の単語があれば出力する。 } *1 ココでは、実は+1バイトして、そこには'\0'をしておいたほうが後々便利。詳細は「C言語 文字列 番兵」でweb検索。 *2 ファイル末尾が'\n'で終わっていることは必要条件です。 *3 というか、strtokの元の文字列を'\0'で分割するという特性から、ここではお勧め。実際のところ、strtokはもともとはコマンドラインをargv(charポインタ配列)に指定するために作られた関数なので、こういう使い方が想定どおりの使い方。

その他の回答 (4)

  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.4

問題は >struct JISYO { >char *hyouki,*yomi; >int hinsi; >}; と、 >jisyo[count2].hyouki[count3]=f; とかですね。 jisyo[count2]で、count2が1の場合、確保したメモリの何バイト目を指定していると思っていますか? 1行目の文字列を格納したあとを指していると考えているかと思いますが。 が、実際には先頭から12バイト後ろを(32bitOSの場合)指しています。 char*4バイト*2、intで4バイトです。sizeofで確認可能です。 で、 >f=jisyo[count2].yomi[count3]; これは読みを格納するつもりなんですから jisyo[count2].yomi[count3] = f; ですよね(^^;;という前提で。 もう気づいたかもしれませんが、 jisyo[count2].yomi[count3]で、count2=0、count3=1のときは、先頭から4バイト目を指しています。つまり、せっかく格納したhyoukiが3バイト以上あったら上書きしちゃってますよね。 といったことが問題になります。あくまでもchar*はアドレスを指すものであって、実体を指すものではありません。 こういった場合なのですが、まず、ファイルの内容をすべて読み込んでバッファに書き込んでしまいます。 で、その読み込んだバッファの中の、どの場所に目的の文字列の先頭があるか?という情報を別の領域を準備し、格納していくのがいいと思います。 もちろん他にも方法は考えられますが。 順序としては、 1.ファイルサイズと、単語の数を調べる。 単語の数は、改行の数でわかりますよね。 #最後の行が改行で終わってるかは確認でいてくださいね。 2.ファイルサイズ分のバッファを確保 3.辞書の管理をする構造体(struct JISYO)のサイズ*単語の数のサイズのメモリを確保 あとは、カンマと改行の位置をファイル内容のバッファを前から調べていき、辞書の構造体の配列に、それぞれの文字列の先頭アドレスを構造体のメンバに格納していってください。 改行、カンマの領域に'\0'を入れていくのを忘れずに。

  • rentahero
  • ベストアンサー率53% (182/342)
回答No.3

まさかとはおもいますが、fseekかまたはrewindでファイルポインタを戻してないからうまくいってないとかそういうことではないでしょうね? それなら、先の回答は無視してください。

  • rentahero
  • ベストアンサー率53% (182/342)
回答No.2

あなたの期待している構造体の形式では、多分期待した動作はできないでしょう。 なぜなら、単語のサイズをどうするのか、余った領域は無駄ではないのか、ということを考えると、以下のようなアルゴリズムを検討してみてください。 1.辞書ファイルをオープンし、サイズを確認する。 2.辞書を全部読み込めるメモリを確保する。 3.行単位でメモリへ読み込み、単語数をカウントする。 4.ファイルをクローズする 5.単語数分のstruct JISYOの配列を確保する。 6.メモリをスキャンし、以下の処理をする。 6-1.辞書のメモリ内から、表記と読みへのポインタをstruct JISYOにセットしていく。その際、カンマを0に置き換えることで文字終端をセットする。 6-2.品詞をstruct JISYOにintにして保存する。 6-3.くりかえす。 7.検索する情報を読み込む 8.配列をチェックし、該当の単語があれば出力する。

  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.1

まず、コンパイルが通るソースを載せてください。 変数の宣言がぜんぜんありません。 >しかし単語数を表示してメモリを確保するところまではできたのですが、辞書の読み込みがうまくいきません。 動的にメモリ確保してるところが無いんですけど。どこですか?

inuiman
質問者

補足

申し訳ありません。字数制限のため『単語の個数,メモリの確保』を省いたんですが一緒に変数の宣言まで消してしまいました。 以下が『単語の個数を数え,メモリを確保』までのソースです。 struct JISYO { char *hyouki,*yomi; int hinsi; }; main(int argc, char *argv[]) { int c,count=0,count2=0,count3=0,hin=0; char f; FILE *fp; struct JISYO *jisyo; fp = fopen(argv[1],"r"); while(c=fgetc(fp)) { if(c==EOF)break; if(c=='\n')count++; } fclose(fp); printf("tangosuu = %d",count); jisyo=(struct JISYO *)malloc(sizeof(struct JISYO)*count); }

関連するQ&A

  • 構造体、ファイル処理のプログラム

    #include<stdio.h> typedef struct stat {   char alph;   int count; }Stat; int main(int argc, char *argv[]){   FILE *fp;   int i=0,j=0;   char rv[1000],c;   Stat tmp,al[26];   for(i=0;i<26;i++){   al[i].alph='a'+i;   al[i].count=0;  } if(argc==2){ fp = fopen(argv[1],"r");  if(fp==NULL){     printf("not found.\n"); exit(1); } } else{ printf("Can not open.\n"); } while((c=fgetc(fp))!=EOF){ c=rv[i]; if( 'A'<=rv[i] && rv[i]<='Z'){ rv[i] = rv[i] + ('a' - 'A'); } i++; } i=0; while(rv[i]!=EOF){ for(j=0;j<26;j++){ if(rv[i] == al[j].alph){ break; }  } al[j].count++; i++; } for(i=0;i<26;i++){ for(j=i+1;j<26;j++){ if(al[i].count > al[j].count){ tmp =al[i]; al[i]=al[j]; al[j]=tmp; }  }  } for(i=0;i<26;i++){ printf("%s %d\n",al[i].alph,al[i].count); } fclose(fp); return 0; } コマンドラインで指定したファイルを読み、その中に出てくるアルファベット(a-z, A-Z)(aとAは同じ文字とカウント)の各文字の文字数をカウントし、カウント結果をソートして、たくさんカウントされたものから順に文字を表示する。 というプログラムなのですが、セグメントエラーとでてしまいます。どこが悪いのか指摘してください。

  • ファイル

    AからZまでの文字が何回出力されるか数えるプログラムなのですが、うまく出力されません。 どこを変えればよろしいでしょうか。 #include<stdio.h> #include<stdlib.h> #include<ctype.h> int count[26]; int main(void) { char str[100] = "xyzYZZ\n"; FILE *fp; char *p; int i; char ch; if((fp = fopen("myfile","w")) == NULL){ printf("ファイルを開くことが出来ません"); exit(1); } p = str; while(*p){ if(fputc (*p,fp) == EOF){ printf("ファイル書き込みエラー"); exit(1); } p++; } fclose(fp); if((fp = fopen ("myfile","r")) == NULL){ printf("ファイルを開くことが出来ません"); exit(1); } while((ch == fgetc(fp)) != EOF){ ch = toupper(ch); if( ch >= 'A' && ch <='Z' ) count[ch - 'A']++ ; } for( i=0 ; i<26 ; i++) printf("%c は %d 回出現\n",i + 'A', count[i]); fclose(fp); return 0; }

  • fscanf

    fscanfを使って、ファイル(普通の英文が入っています)から一単語ずつ読み込んでいきたいと思っているのですが、どうすれば良いのかわかりません。 int main(int argc,char *argv[]){ FILE *fp; char *word; fp = fopen(argv[1], "r"); if(fp == NULL){ printf("error: not open file.\n"); return(0) ;} while( ){ fscanf(fp,"%[a-zA-Z]",word); printf("%s\n", word); fflush(stdout); } fclose(fp); return 0; } とりあえず上の様なプログラムで、一単語ずつ順番に単語を出力できるようにしたいと思っているのですが。 いろいろ変なところなど在ると思いますが、whileの条件など、どうすればよいか教えてください。

  • ファイルから一文字ずつ読み込む

    ファイルを読み込むfgetc()関数のところでプログラムが停止します。 以下が実行したプログラムです。 #include<stdio.h> void get_name(char name[],int a){ printf("ファイル名を入力してください。\n"); scanf("%s",name); } void open(FILE *fp2,char name[]){ if((fp2=fopen(name,"r"))==NULL){ printf("ファイルオープンエラー"); } } int count(FILE *fp3){ int ch=0; int count=0; if(fp3==NULL){ printf("error"); } while((ch=fgetc(fp3))!=EOF){ if(ch=='\n'){ count++; } } printf("TEST"); fclose(fp3); return(count); } int main (void){ FILE *fp; char fname[30]; get_name(fname,30); open(fp,fname); printf("%d",count(fp)); return(0);} ファイル名を入力してください。ファイル名を入力、プログラム停止です。 '\n'を数えれるようにしてください。御指摘お願いします。

  • C言語 複数指定したファイルの内容表示

    C言語の質問です。 「実行したファイル名を除いたコマンド ライン引数に、 複数個指定したすべてのファイルの内容を標準出力に表示する」 というプログラムを作成しようと思うのですが。。。 どうしても上手くいきません。 1つのファイルの内容を標準出力に表示するプログラムになってしまいます。 どなたかお力を貸してください。 ちなみに、私が作成したプログラムは、以下です。↓↓ #include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; int c; if(argc != 2) { return 1; } if((fp=fopen(argv[1], "r"))==NULL) { return 1; } while((c=fgetc(fp)) !=EOF) { fputc(c, stdout); } fclose(fp); return 0; }

  • C言語でファイルから読み込みができません。

    以下のプログラムを実行すると よくわからない値が返ってきます。 なぜでしょうか?教えてください。 #include <stdio.h> void main () { FILE *fp; double a, b; int count=0; fp = fopen("test.xls", "r"); while(1) { fscanf(fp, "%lf %lf\n", &a, &b); printf("%f, %f\n", a, b); count++; if(count==10)break; } fclose(fp); } test.xlsの中身は 1.0 2.0 3.0 4.0 5.0 6.0   :   :   :   : です。 よろしくお願いします。

  • C言語 ファイルの読み込みについて

    以下のようなプログラムを実行します \nで改行が行われません。なぜでしょうか? #include <stdio.h> void main () { FILE *fp; double a, b; int count=0; fp = fopen("arm_x.csv","r"); while(1) { fscanf(fp,"%lf%lf\n",&a,&b); printf("%f::%f\n",a,b); count++; if(count==10)break; } fclose(fp); } csvファイルは 0.0 0.1 0.2 0.3 0.4 0.5 0.1 0.2 0.3 0.4 0.5・・・ 0.2 0.3 0.4・・・・・・・・・ : : です。 実行結果はこのようになってしまいます。 0.0 0.1 0.2 0.3 0.4 0.5 : :

  • エラーの意味

    このプログラムは、三番目のコマンドライン引数に、 watch が入力されると、画面に文字表示されるはずですが、 おそらく if ( argv [3] == 'a' ) putchar ( c ) のところで、エラーが出ます。 文字列定数は 、1 文字か 2 文字でなければならない ( 関数 main ( int,char * * ) ) ' int ' 型は 、' char * ' 型に変換できない ( 関数 main ( int,char * * ) ) このエラーの意味が解らないのですが、何を伝えたいのか解説をお願いします。 int main(int argc,char *argv[]) { FILE *fp ,*fp1; int c; fp=fopen(argv[1],"r"); if(argv[1]==NULL){ printf("no open"); exit(1); } fp1=fopen(argv[2],"w"); if(argv[2]==NULL){ printf("no open"); exit(1); } while ( ( c=fgetc ( fp ) ) !=EOF ) { fputc ( c , fp1 ) ; if ( argv[3] == ' watch ' ) putchar ( c ) ; }

  • C言語の質問です

    下記のプログラムはテキストファイルを読み込み、AからZまでの文字(小文字、大文字は区別しない)がそれぞれ何回 現れたかを数えるプログラムです。 #include <stdio.h> #include <stdlib.h> #include <ctype.h> int count[26]; int main(int argc, char *argv[]) { FILE *fp; char ch; int i; /* ファイル名の指定を調べる */ if(argc!=2) { printf("ファイル名の指定がありません\n"); exit(1); } if((fp = fopen(argv[1], "r"))==NULL) { printf("ファイルを開くことができません\n"); exit(1); } while((ch=fgetc(fp))!=EOF) { ch = toupper(ch); if(ch>='A' && ch<='Z') count[ch-'A']++; } for(i=0; i<26; i++) printf("%c は %d 回出現\n", i+'A', count[i]); fclose(fp); return 0; } 1)int count[26]; で、なぜ26なのかが分かりません。 2)count[ch-'A']++; はどういう動作をするのか詳しく教えてほしいです。 3)よって、for文がどういう動作で表示しているのかが分かりません。 未熟者の私ですが、どなたか教えていただけないでしょうか?

  • 構造体の動的メモリについて

    #include <stdio.h> #include <stdlib.h> #include <string.h> #define NUM 10 /*生徒数*/ typedef struct data { int num; /*従業員番号*/ char name[16]; /*名前*/ int jap; /*国語の点数*/ int math; /*数学の点数*/ }data; int main(void) { data test[NUM]; /*生徒のデータ*/ FILE *fp; /*ファイル操作用*/ int i; /*配列インデックス*/ int count; /*データ分割用*/ char s[100]; /*データ読み込み用*/ char *token; /*データ分割用*/ char *data[6]; /*分割データ保存用*/ fp = fopen ("data1.txt","r");        for (i=0; i<NUM; i++){ fgets (s, sizeof(s), fp); /*dataファイルから1行読み込み*/ token = strtok(s, ","); /*読み込んだデータを","で分割する*/ count=0; while (token != NULL){ data[count] = token; /*分割したデータを配列へ入れる*/ token = strtok(NULL,","); count++; } test[i].num = atoi(data[0]);       strcpy (test[i].name, data[1]); test[i].jap = atoi(data[2]); test[i].math = atoi(data[3]); } fclose(fp);       } このプログラムを構造体へのポインタの配列で管理するように変えたいのですが、どのように変更すればいいのでしょうか。 条件として、個人データを格納するメモリ領域とポインタの配列のメモリ領域は動的に確保します。 初心者でよくわからないので詳しく教えていただけると助かります。よろしくお願いします。