• ベストアンサー

単語の出現回数を数え、出現回数順に表示するには?

標準入力または入力ファイルから単語の出現回数を調べるプログラムの一部です。ENTRY *add_entry(ENTRY *new)関数で以前に出てきた単語か(正確には以前に同一内容の行があったかを)判断し、既出ならばvoid do_one(FILE *fp)関数内部でp->count++として出現回数に加算しています。void do_one(FILE *fp)関数の最後の方のprintfで単語とその出現回数を表示します。これだとappleという単語が2回出てくれば、 apple:1 apple:2 と表示されます。この場合は apple:2 だけ表示させ、さらに、出現回数の多い順に単語を並べて表示させたいんです。どういう風にすればいいでしょうか? typedef struct entry_t { char *str; struct entry_t *next; int count; /* 単語の出現回数 */ } ENTRY; int main(int argc, char **argv) { FILE *fp; char *s; init_hash(); --argc; ++argv; if (argc == 0) do_one(stdin); else { while (argc--) { if ((fp = fopen(*argv, "r")) == NULL) cant(*argv); /* no return */ do_one(fp); fclose(fp); argv++; } } free_all_entries(); return (0); } void do_one(FILE *fp) { ENTRY *p; char buf[MAX_SIZE]; int i; while (fgets(buf, MAX_SIZE, fp) != NULL) { if ((p = malloc(sizeof(ENTRY))) == NULL) error("can't alloc memory"); /* no return */ if ((p->str = strdup(buf)) == NULL) error("can't alloc memory"); /* no return */ if (add_entry(p) != p) { p->count++; /* 登録済みなので単語の出現回数を1増やす */ free(p->str); /* すでに登録されているので開放 */ free(p); } else /* 新しく単語を登録する場合 */ p->count =1; /* 単語の出現回数を1にする */ printf("%s : %d\n", p->str, p->count); } } void init_hash(void) { int i; for (i = 0; i < HASHSIZE; i++) table[i] = NULL; } ENTRY *add_entry(ENTRY *new) { ENTRY *p; int h; h = hash(new->str); /* 追加する単語のハッシュ値を求める */ for (p = table[h]; p != NULL; p = p->next) if (strcmp(p->str, new->str) == 0) { /* 追加済みの単語ならば */ return (p); /* そのデータのポインタを返す */ } new->next = table[h]; table[h] = new; /* 単語を追加する */ return (new); }

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

  • ベストアンサー
  • yama5140
  • ベストアンサー率54% (136/250)
回答No.2

(英単語限定!) ★配列を使ったら「簡単」では・・?  (短所:オーバーフロー(▼)の都度、要調整) #define MAXCNT (2000) // ▼ #define un_char unsigned char // 大小比較あるため int igStoreCnt = 0; // 出現単語カウンタ(グローバル) typedef struct{  un_char cStr[16]; // 1単語16文字以内  int iCount; // 当単語の出現回数 }ENTRY; ENTRY sStore[MAXCNT]; // 単語収録(▼) ★まず読み込んだ1行を単語毎(◆)に分割 int OneLineWordCount( un_char cBuf[] ) {  int i, iTop, iLen = 0;  for( i = 0; i < 2048; i++ ){ // レコード長 2048 以内   if( 0x09 == cBuf[i] ) cBuf[i] = 0x20; // タブ   if( 0x0D >= cBuf[i] ){ // 行末    if( iLen ) WordCount( iTop, cBuf, i ); // ◆    return( 0 );   }   if( 0x27 == cBuf[i] ) continue; // 例) it's の '   if( ! isalpha( cBuf[i] ) ){ // デリミタ    if( 0 == iLen ) continue;    WordCount( iTop, cBuf, i ); // ◆    iLen = 0;    continue;   }   if( 0 == iLen ) iTop = i;   iLen++;  }  return( 0 ); } ★カウント部分 int WordCount( int iTop, un_char cBuf[], int iii ) {  int k;  cBuf[iii] = 0x00;  for( k = 0; k < igStoreCnt; k++ ){   if( NULL == strcmp( sStore[k].cStr, &cBuf[iTop] ) ){    sStore[k].iCount++; // 既存    return( 0 );   }  }  strncpy( sStore[igStoreCnt].cStr, &cBuf[iTop], 16 );  sStore[igStoreCnt].iCount = 1; // 新単語  igStoreCnt++;  if( MAXCNT < igStoreCnt ) ErrorStop( "Over" ); // ▼  return( 0 ); } ★出力部分(同数の場合出現順) void Ranking( void ) {  int k, j, iMax, iBasyo;  for( k = 0; k < igStoreCnt; k++ ){   iMax = 0;   for( j = 0; j < igStoreCnt; j++ ){    if( sStore[j].iCount > iMax ){     iMax = sStore[j].iCount;     iBasyo = j;    }   }   printf( "%4d ", k );   printf( "%-16s", sStore[iBasyo].cStr );   printf( "%3d\n", sStore[iBasyo].iCount );   sStore[iBasyo].iCount = 0; // 用済み  } } void ErrorStop( un_char *cMess ) {  fprintf( stderr, "Err %s\n", cMess );  exit( 0 ); } 注:インデントに全角空白を使用しています。

lucky_sta
質問者

お礼

具体的な説明ありがとうございました。じっくり考えてやってみます。

その他の回答 (1)

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.1

do_one関数の中で出現回数を出力するのではなく、 線形リストを先頭から順に走査して回数を出力する関数を新たに設けて、 do_one関数の後で実行すればよいと思います。 出現回数によるソートの考え方は、線形リスト以外のデータ構造、 例えば配列、の場合と同じです。 ポインタの張り替えがややこしいかもしれませんけれど。

lucky_sta
質問者

お礼

どうもありがとうございました。じっくり考えて見ます。

関連するQ&A

  • C言語に関する質問

    初めて質問させて頂きます。C言語初心者です。 実は講義で「ファイル中の英文を単語に分けてその出現頻度をカウントするコードを木構造を用いて出力せよ」という課題が出ました。 そこで、参考にするコードを検索しましたところ、以下のURLにあるベストアンサーのコードが近いと感じました。 http://okwave.jp/qa/q4155655.html コードの内容は以下の通りになります。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> typedef struct node Node; struct node{ char *word; int count; Node *left,*right; }; Node *root=NULL; void compose(FILE *fp); void inorder(Node *p); void strlower(char *s); int main(int argc, char *argv[]) { FILE *fp; Node *new; fp=fopen(argv[1],"r"); if(fp==NULL){puts("ファイルを開けません");return(-1);} compose(fp); inorder(root); return (0); } void strlower(char *s){ while(*s!=NULL){*s=tolower(*s);s++;} } void compose(FILE*fp){ Node **p,*new; char buf[256]; while(1){ fscanf(fp,"%[^a-zA-Z0-9]",buf); if(fscanf(fp,"%[a-zA-Z0-9]",buf)==EOF)break; strlower(buf); if(root==NULL){ new=(Node *)malloc(sizeof(Node)); new->left=NULL; new->right=NULL; new->word=strdup(buf); new->count=1; root=new; }else{ *p=root; while(1){ if(strcmp(buf,(*p)->word)==0){ (*p)->count++;break; }else if(strcmp(buf,(*p)->word)<0){ if((*p)->left==NULL){ new=(Node *)malloc(sizeof(Node)); new->left=NULL;new->right=NULL;new->word=strdup(buf);new->count=1; (*p)->left=new; break; }else{ *p=(*p)->left; } }else{ if((*p)->right==NULL){ new=(Node *)malloc(sizeof(Node)); new->left=NULL; new->right=NULL; new->word=strdup(buf); new->count=1; (*p)->right=new; break; }else{ *p=(*p)->right; } } } } } } void inorder(Node*p){ if (p==NULL) return; inorder(p->left); printf("%s %d\n",p->word, p->count); inorder(p->right); } しかし、これをそのままコンパイル・実行すると、コンパイル時に以下の注意が出ます。 warning comparison between pointer and integer ('int' and 'char *') while(*s!=NULL){*s=tolower(*s);s++;} 上記の注意を無視してそのまま実行すると、segmatation faultが出てしまいますorz おそらく、sの型が*s=s[]なので、注意の中の「s++」の部分で誤作動を起こしている(s++を実行するにはsはint型でなければならない)と思うのですが、どうコード文を変えれば良いのかがよくわかりません。 どなたかお教え頂けると幸いです。どうぞよろしくお願いしますm(_ _)m

  • 二分探索木への挿入

    今学校で二分探索木を勉強しています。二分探索木に要素を挿入したいのですが、うまくいかないのでアドバイスをいただけないでしょうか。ファイル中の英文を単語に分けてその出現頻度をカウントするプログラムです。とりあえず二分探索木を作るところまではなんとか完成させたいです。 #include <stdio.h> #include <stdlib.h> #include<string.h> #include <ctype.h> typedef struct node Node; struct node{ char *word; int count; Node *left,*right; }; Node *root=NULL; Node *compose(FILE *fp); void inorder(Node *p); int main(int argc, char *argv[]) { FILE *fp; Node *new; fp=fopen(argv[1],"r"); if(fp==NULL){ puts("ファイルを開けません"); return(-1); } new=(Node *)malloc(sizeof(Node *)); new=compose(fp); inorder(new); return (0); } Node *compose(FILE *fp) { Node **p,*new; char buf[20]; p=&root; while(fscanf(fp,"%[-a-z-A-Z0-9_]",buf)!=EOF){ while(*p!=NULL){ (*p)->count=0;  /*countを0で初期化したいけどwhileの外にこの1行を出すとエラーが出る*/ buf[0]=tolower(buf[0]); strdup(buf); if(strcasecmp(buf,(*p)->word)==0){   /*if文にしかはいらない…*/ (*p)->count++; printf("%d\n",(*p)->count); }else if(strcasecmp(buf,(*p)->word)<0){ p=&(*p)->left; }else{ p=&(*p)->right; } } new=(Node *)malloc(sizeof(Node *)); new->left=NULL; new->right=NULL; new->word=buf; *p=new; fscanf(fp,"%*[^-a-z-A-Z0-9_]"); } return(new); } void inorder(Node *p) { if(p==NULL) return; printf("%s",p->word); if(p->count!=0){ printf("%d",p->count); } inorder(p->right); inorder(p->left); }

  • c言語で文書を読み込み、単語の出現頻度を教える

    c言語の課題で、与えられた文書を読み込んでその中にある単語の出現頻度を教えるプログラミングを作成しているのですが、うまくいきません。 どこが間違っているのでしょうか?? ファイルの中身は As sweet as coat , green , milk And everyone think coat になっており、求めたい回答は buffer[0]=As count=1 buffer[1]=sweet count=1 buffer[2]=as count=1 buffer[3]=coat count=2 buffer[4]=green count=1 buffer[5]=milk count=1 buffer[6]=And count=1 buffer[7]=everyone count=1 buffer[8]=think count=1 Number of words:9 にしたいのです。 プログラミングは #include<stdio.h> #include<stdlib.h> #include<string.h> #include<ctype.h> typedef struct token_checker{ char * token; int count; }TOKEN_CHECKER; int main(int argc,char* argv[]){ FILE * fp; char buffer[1024]; int numword=0; char * delimiter = " .,"; char * s; TOKEN_CHECKER *t; int j; int i = 0; int flag =0; int find = -1; t=(TOKEN_CHECKER*)malloc(sizeof(TOKEN_CHECKER)*24); if(argc != 2){ printf("Parameter error.\n"); exit(1); } if((fp=fopen(argv[1],"r"))==NULL){ printf("File open error.\n"); exit(1); } while( fgets(buffer,sizeof(buffer),fp)!=NULL){ buffer[ strlen(buffer)-1]='\0'; s = strtok(buffer,delimiter); if(s != NULL){ t[numword].token=s; for(i=0;i<=numword;i++){ if(strcmp(t[numword].token,t[i].token)==0){ t[i].count++ } } printf("buffer[%d]=%s count=%d\n",numword,t[numword].token,t[numword].count); numword++; } while((s=strtok(NULL,delimiter)) != NULL){ t[numword].token=s; for(i=0;i<=numword;i++){ t[numword].count=0; if(strcmp(t[numword].token,t[i].token)==0){ t[i].count++; } } printf("buffer[%d]=%s count=%d\n",numword,t[numword].token,t[numword].count); numword++; } } printf("Number of words:%d\n",numword); free(t); fclose(fp); exit(0); } になっており、実行すると buffer[0]=As count=1 buffer[1]=sweet count=1 buffer[2]=as count=1 buffer[3]=coat count=1 buffer[4]=green count=1 buffer[5]=milk count=1 buffer[6]=And count=1 buffer[7]=everyone count=1 buffer[8]=think count=1 buffer[9]=coat count=1 Number of words:9 となってしまい、coatがカウントされないのです。

  • エラーが出ます

    12,51,13,123,133,551,… というコンマで区切られているファイルを読み込みたいです。数字は100文字です。どう書けば全部読み込めるでしょうか? よろしくお願いします。 int main(int argc,char *argv[ ]){ int line; int i; FILE *fp; fp = (argc > 1)? fopen(argv[1],"r"): stdin; if(fp==NULL){ perror("fopen"); exit(0); }   while(fscanf(fp,"%d",&line) != EOF){ printf("%d\n") } if(argc>1){ fclose(fp);   }

  • 英文字の出現頻度

    英語で書かれているファイルを読み込んで英文字の出現頻度を調べるプログラムを作ったのですが、ところどころ正確な数が表示されません。どなたか教えてもらえませんか?欲を言えばスペースや改行もカウントできれば良いのですが・・・よろしくお願いしますm(_ _)m #include <stdio.h> #define N 500 int main(int argc, char *argv[]) { int c,i=0; int X[N]; FILE *fp; char ch[N]; if( argc != 2) { printf("使い方:コマンド名に続きファイル名を入れてください\n"); return -1; } fp = fopen( argv[1],"rb"); while(fgets(ch,500,fp) != NULL ) { printf("%sファイルにおけるアルファベットの出現頻度\n",argv[1],ch); { while ((c = getc(fp)) != EOF) if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') X[c]++; { for (c = 'a'; c <= 'z'; c++) printf("%c:%d\n",c, X[c]); printf("\n"); for (c = 'A'; c <= 'Z'; c++) printf("%c:%d\n",c, X[c] ); printf("\n"); } } } fclose(fp); return 0; }

  • 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文がどういう動作で表示しているのかが分かりません。 未熟者の私ですが、どなたか教えていただけないでしょうか?

  • C言語、ファイル操作、fgets()について

    次のプログラムは入力された行を読み込み、コマンドラインで指定されたファイルに書き込みます。 空白行が入力されたら、入力の終了とみなしてファイルを閉じます。続いてファイルを入力用に開き、 fgets()を使ってファイルの内容を表示するものです。 (ソースコードが長くてすみません) #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { FILE *fp; char str[80]; /* コマンドライン引数を検査する */ if(argc!=2) { printf("ファイル名を指定してください\n"); exit(1); } /* 出力用にファイルを開く */ if((fp = fopen(argv[1], "w"))==NULL) { printf("ファイルを開くことができません\n"); exit(1); } printf("終了するには空白行を入力してください\n"); do { printf(": "); gets(str); strcat(str, "\n"); /* 改行を追加する */ if(*str != '\n') fputs(str, fp); } while(*str != '\n'); fclose(fp); /* 入力用にファイルを開く */ if((fp = fopen(argv[1], "r"))==NULL) { printf("ファイルを開くことができません\n"); exit(1); } /* ファイルを読み込み直す */ do { fgets(str, 79, fp); if(!feof(fp)) printf(str); } while(!feof(fp)); fclose(fp); return 0; } 【質問】fgets()内のint型の数値「79」がどうして79なのかが分かりません。     80でも良いような気がするのですが・・・

  • c言語でデータの指定列の度数分布をつくろうとしています。

    c言語でデータの指定列の度数分布をつくろうとしています。 以下のように1列のデータに対してはプログラムを組むことができます。 #include<stdio.h> #include<stdlib.h> #define UPPERBOUND 2.0 #define LOWERBOUND -2.0 #define RANGE 0.1 int main(int argc, char* argv[]){ int i; FILE *fp; FILE *fp2; char str[256]; if(argc != 2 && argc != 3){ printf("***usage: %s <datafilename>\n", argv[0]); exit(-1); } if((fp = fopen(argv[1], "r")) == NULL){ printf("can't open file\n"); exit(-1); } if(argc == 3){ if((fp2 = fopen(argv[2], "w")) == NULL){ printf("can't open outputfile\n"); exit(-1); } } int tablesize = (UPPERBOUND-LOWERBOUND)/RANGE; int table[tablesize]; for(i=0; i<tablesize; i++){ table[i] = 0; } while((fgets(str, 256, fp)) != NULL){ double dbl = atof(str); if(!(dbl < LOWERBOUND || dbl > UPPERBOUND)){ int n = (dbl-LOWERBOUND)/RANGE; table[n]++; } } for(i=0; i<tablesize; i++){ printf("%lf~%lf\t: %d\n", LOWERBOUND+(i*RANGE), LOWERBOUND+((i+1)*RANGE), table[i]); if(argc == 3){ fprintf(fp2, "%lf~%lf\t: %d\n", LOWERBOUND+(i*RANGE), LOWERBOUND+((i+1)*RANGE), table[i]); } } return 0; } そこで複数列のデータに対して、例えば10列100行のデータに対して6列目だけを見たいときはどうすればできますでしょうか?

  • 2分木のノードの指定方法を変えたい

    下のプログラムはコマンドライン引数できまった形で入力した数を2分木にしてそれを表示させるc言語のプログラムです。 きまった形というのは 短縮形は [ 8 [ 7 2 5 ] [ 3 1 _ ] ] 短縮形でないのは [ 8 [ 7 [ 2 _ _ ] [ 5 _ _ ] ] [ 3 [ 1 _ _ ] _ ] ] ] のような形で、実行結果はそれぞれ a.exe [ 8 [ 7 2 5 ] [ 3 1 _ ] ] 入力データ [ 8 [ 7 2 5 ] [ 3 1 _ ] ] a.exe [ 8 [ 7 [ 2 _ _ ] [ 5 _ _ ] ] [ 3 [ 1 _ _ ] _ ] ] ] 入力データ [ 8 [ 7 2 5 ] [ 3 1 _ ] ] のようになります。 これをこのような形ではなくコマンドラインで 8 7 3 2 5 1 と入力するだけで2分木になるように 下のプログラムを変えたいのです。 よろしくお願いします。 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef int BITREE_TYPE; /* 格納データの型 */ struct node { BITREE_TYPE value; /* ノードの値 */ struct node *left; /* 左ノードのアドレス */ struct node *right; /* 右ノードのアドレス */ }; typedef struct node BITREE_NODE; void error(char *msg); BITREE_NODE *createNode(BITREE_TYPE x); void destroyBITree(BITREE_NODE *p); int isLeafNode(BITREE_NODE *p); void printBITree(BITREE_NODE *p, int tabs, int brief); BITREE_NODE *inputBITree(char *str[], int len, int *end); int gShortFormat = 1; /*1ならば短縮形で出力する*/ void error(char *msg){ fflush(stdout); fprintf(stderr,"%s\n", msg); exit(1); } BITREE_NODE *createNode(BITREE_TYPE x){ BITREE_NODE *new; new = malloc(sizeof(struct node)); if(new == NULL) error("createNode: メモリがありません"); new->value = x; new->left = NULL; new->right = NULL; return new; } void destroyBITree(BITREE_NODE *p){ if(p == NULL) return; destroyBITree(p->left); destroyBITree(p->right); memset(p, 0, sizeof(struct node)); free(p); } int isLeafNode(BITREE_NODE *p){ return(p->left == NULL) && (p->right == NULL); } void printSubtree(BITREE_NODE *p){ if(p == NULL){ printf("_"); return; } if(gShortFormat != 0 && isLeafNode(p)){ printf("%d", p->value); } else{ printf("[ "); printf("%d ", p->value); printSubtree(p->left); printf(" "); printSubtree(p->right); printf(" ]"); } } void printBITree(BITREE_NODE *p, int tabs, int brief){ int i; gShortFormat = brief; for (i = 0; i<tabs; i++) printf("\t"); printSubtree(p); printf("\n");fflush(stdout); } BITREE_NODE *inputBITree(char *str[], int len, int *end){ BITREE_NODE *p; int i =0; if(len < 1) error("inputBITree:データがありません"); /*短縮形の処理*/ if(strcmp(str[0], "[") != 0){ if(strcmp(str[0], "_") == 0) error("inputBTITree:値に_は指定できません"); *end =1; return createNode(atoi(str[0])); } p = createNode(atoi(str[1])); i = 2; if(strcmp(str[i], "_") != 0){ p->left = inputBITree(&str[i], len -i, end); i+= *end; } else{ i++; } if(strcmp(str[i], "_") != 0){ p->right = inputBITree(&str[i], len -i, end); i+= *end; } else{ i++; } if(strcmp(str[i], "]") != 0) error("inputBITree: 入力データが]で終わっていません"); *end = i + 1; return p; } int main(int argc, char *argv[]){ BITREE_NODE *p; int end = 0; p = inputBITree(argv+1, argc-1, &end); printf("入力データ "); printBITree(p,0,1); destroyBITree(p); return 0; }

  • 修正おねがいします。

    コンパイルして得られる実行ファイルを list1 として、 %list1 文字列1 文字列2 ・・・ を実行すると文字列1、文字列2、・・・を連結した一つの文字列を動的に生成した領域に格納し、それをディスプレイ上に表示するプログラムを作成しようと思います。 #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char *argv[]) { char s[50]; int n = 0; int count = 0; char *str; if(argc < 2) puts("連結できませんでした"); else{ for(n = 1; n < argc; n++) { str = argv[n]; for(;*str != '\0'; count++) { s[count] = *str; str++; } } } s[count] = '\0'; str = malloc(strlen(s) + 1); if(str != NULL){ strcpy(str, s); printf("%s\n", s); free(str); } } これでは実行できません。どこをどう修正すべきでしょうか? また、まったく違うのであれば正しいのを教えてください。 すいません。よろしくお願いします。

専門家に質問してみよう