C言語関係掲示板

過去ログ

No.1070 ファイルより単語を読み取り、条件により別のファイル保存するプログラム

[戻る] [ホームページ]
No.14058

ファイルより単語を読み取り、条件により別のファイル保存するプログラム
投稿者---サミー(2004/05/16 12:39:24)


はじめまして。サミーと申します。現在学校の課題に取り組んでいるのですが、どうしても自力で解けない問題が発生してしまい書き込みをしています。

ひらがな(小文字を除く)の辞書ファイルdict.txtがあり、dict.txtには一行に一単語が書き込まれていてあいうえお順に並べられています。
一番最初の単語は「あー」で最後の単語は「わんわん」です。ここで課題を解くにあたって

「テキストファイルdict.txtを先頭より順次読み込んでゆき、文字列を分類し、対応するファイルに保存していく」
という操作を実行するプログラムを作成したいと思っています。

各単語は分類してout-m-n-o.txt(m,n,oは数字)というファイルに保存します。
m = 先頭のひらがな(「あ」= 0,「い」=1 ・・・ 「か」= 6 ・・・などのようにひらがなに数字を対応させる)
n = 文字数
l = 文字中のひらがなの重複情報(例えば1文字目と3文字目が重複していたら l = 2^0 + 2^2 = 5)

動作例(以下の処理を辞書に含まれる全ての単語について行う)
「あいうえお」-> 「out-0-5-0.txt」に保存(重複なし)
「きき」-> 「out-7-2-3.txt」に保存(1文字目と2文字目が重複しているので l = 1+2 = 3)
「かかいうえい」->「out-6-6-39.txt」に保存(「あ」と「い」が複数含まれるので l = 1+2+4+32 = 39)

上記のような動作を実行するために過去ログを参考にして次のコメントにあるような(長すぎて入りきりませんでした。申し訳ありません。)
プログラムを作成したのですが、cygwin上でgccを用いてコンパイルし、実行をするたびに SEGMENTATION FAULT で実行が止まってしまいます。
デバッガを使用してバグを確認しようとしたのですが、何が直接の原因となっているのか全く分かりませんでした。
途方にくれています。どなたか助言をいただけると大変ありがたいです。

尚下記のソースのgetnumberは重複配置情報を得るための関数で、separateは分割保存を実行するための関数です。よろしくお願いします。




No.14059

↑のプログラムです。
投稿者---サミー(2004/05/16 12:40:24)


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define STR_MAX 256    /* 文字列入力用配列長 */
#define MAX_LEN 9   /* 辞書に含まれる最長の単語の長さ */
#define TABLESIZE 1000 

struct box{
  FILE *fp;
  int length;
  int number;
};

int power(int m, int n)
{
    int i, re = 1;

    for(i = 0; i < n; i++){
        re *= m;
    }

    return re;
}

int getnumber(char *str)
{
        char    *let;                 /* 一致する場合 */
        char    *sp;
        char   *save[strlen(str)];
        int i,j,found = 0,smax=0,num = 0,count,first;
      
        
      let = str;
      
      for(i=0; i < strlen(str) ; i = i+2, let = let +2 ){

      found = 0;
      for(j=0;j < smax;j++){
            if( strncmp(let,save[j],2) == 0){
              found = 1;
              break;
            }
      }
    
      if( found == 0){
        save[j]=(char *)malloc(3);
            strcpy(save[j],let);
            smax++;
              
       for(j=0, count= 0, first = 0, sp = str; j < strlen(str) ; j = j+2,sp = sp +2 ){
        if( strncmp(let,sp,2) == 0){
         if(count >= 1){
              num += power(2,j/2);  
              count++;
            }
            else{
              count++;
              first = j;
            }
              }
            } 

            if(count>1) num += power(2,first/2);
            
          }
          
    }
        
  return num;
}

void separate(FILE *fi, char *fname, char *word){
    struct box table[TABLESIZE]; 
    fpos_t fpos;
    int i,j;
    char buff[STR_MAX];        /* 文字列用 */
    char filename[STR_MAX];  /* ファイル名 */
    int found,length,number;
    int tsize = 0;


    while(fgets(buff, STR_MAX, fi) != NULL){    
       if(strncmp(buff,word,2) == 0){
       
        found = 0;
        
        for (i = 0; i < tsize ; i++){
         if(table[i].length == strlen(buff)/2-1 && 
          table[i].number == getnumber(buff)){
          
          fprintf(table[i].fp,"%s",buff);
          found = 1;
          break;
         }
        }
        
        if (found == 0){
         length = table[tsize].length = strlen(buff)/2-1;
         number = table[tsize].number = getnumber(buff);

         sprintf(filename,"%s-%d-%d.txt",fname,length,number);
                
         if((table[tsize].fp = fopen(filename, "w")) == NULL){    /*出力ファイルオープン */
              printf("入力ファイルがオープンできません\n");
              exit(1);        /* 強制終了 */
          }
         fprintf(table[tsize].fp,"%s",buff);
         tsize++;
        }
        
        fgetpos(fi, &fpos);
        
       }
       else break;
    }

    fsetpos(fi, &fpos);
    for (i = 0; i < tsize ; i++) fclose(table[i].fp);
}

int main(void)
{
    FILE *fi;             /* ファイルポインタ用 */
    char word[3];      /* 検索ひらがな用*/
    char fname[STR_MAX];
    int ncount = 0;   /*ファイル名の番号*/
    int count = 0;      /*検索用単語のアドレス操作のため*/

    if((fi = fopen("dict.txt", "r")) == NULL){    /* 入力ファイルオープン */
        printf("入力ファイルがオープンできません\n");
        exit(1);        /* 強制終了 */
    }

  while(strcmp(word,"わ") != 0){
    strcpy(word,"あ");
    word[1]=word[1]+count;
    
    sprintf(fname,"out-%d",ncount);
    
    if(strcmp(word,"ぃ") == 0 || strcmp(word,"ぅ") == 0 || 
      strcmp(word,"ぇ") == 0 || strcmp(word,"ぉ") == 0 || 
      strcmp(word,"っ") == 0 || strcmp(word,"ゃ") == 0 || 
      strcmp(word,"ゅ") == 0 || strcmp(word,"ょ") == 0 ||
      strcmp(word,"ゎ") == 0){}
    else{ 
      separate(fi,fname,word);
      ncount++;
    }
    count++;
  }
    
    fclose(fi);    /* 入力ファイルクローズ */
    return(0);
}



No.14061

Re:↑のプログラムです。
投稿者---あかま(2004/05/16 16:16:35)


実行してないからなんともいえませんが
char word[3];
が初期化されてないからstrcmpで変なとこ参照されたりしてませんか?

word[1]=word[1]+count;
これはなにをやってるんでしょう?
('A'+1) == 'B'
みたいな感じでしょうか?
2バイト文字でやるとうまくいかない気がします。文字コードがなにかもわかりませんし。
2バイト文字をグリグリ動かすのは相当難しいですよ。

char *hiragana[]={"あ","い","う","え","お",…}
みたいなテーブルを作って、

int num =sizeof(hiragana)/sizeof(char *);
for(i=0;i < num;i++){
    separate(fi,fname,hiragana[i]);
}
こんな感じで参照したらどうでしょうか?
めんどいですが確実です。




No.14062

Re:↑のプログラムです。
投稿者---サミー(2004/05/16 17:09:04)


さっそくのお返事ありがとうございます。確かにあかま様の指摘されたとおりなので以下のようにmain関数を変更しましたが、
今だにシグメンテーションエラーが発生してしまいます。デバッガで見てみるとseparate関数内のsprintf周辺でシグナルが発生しているようです。
引き続き助言をお願いします。

int main(void)
{
   FILE *fi;             /* ファイルポインタ用 */
   char fname[STR_MAX];
   char *hiragana[] = {"あ","い","う","え","お","か","が","き","ぎ","く","ぐ","け","げ","こ","ご",
                              "さ","し","す","せ","そ","ざ","じ","ず","ぜ","ぞ","た","ち","つ","て","と",
                              "だ","ぢ","づ","で","ど","な","に","ぬ","ね","の","は","ば","ぱ","ひ","び",
                              "ぴ","ふ","ぷ","ぶ","へ","べ","ぺ","ほ","ぼ","ぽ","ま","み","む","め","も",
                              "や","ゆ","よ","ら","り","る","れ","ろ","わ","ん"};
   int ncount = 0;      /*ファイル名の番号*/
    int i;
   int num =sizeof(hiragana)/sizeof(char *);
                                

   if((fi = fopen("dict.txt", "r")) == NULL){    /* 入力ファイルオープン */
    printf("入力ファイルがオープンできません\n");
      exit(1);        /* 強制終了 */
    }

    for(i=0;i < num;i++){
            sprintf(fname,"out-%d",num);
            separate(fi,fname,hiragana[i]);
        }
        
    fclose(fi);    /* 入力ファイルクローズ */
    return(0);
}



No.14063

Re:↑のプログラムです。
投稿者---あかま(2004/05/16 18:13:49)


で、面白そうだったので作ってみました。
ひらがなのテーブルはめんどくさかったので途中まで。。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define TABLE_SIZE sizeof(hiragana)/sizeof(char *)
#define WORD_MAX 256

char *hiragana[]={//めんどくさいので途中まで
    "あ","い","う","え","お","か","き","く","け","こ",
    "さ","し","す","せ","そ","た","ち","つ","て","と",
    "な","に","ぬ","ね","の","は","ひ","ふ","へ","ほ",
    };

//ひらがなテーブルをi番目から探索しテーブル番号を返す.無いときは-1.
int search_table(int i,char *word){
    char initial[3];
    initial[0]=word[0];
    initial[1]=word[1];
    initial[2]='\0';
    
    for(;i < TABLE_SIZE;i++){
        if(strcmp(hiragana[i],initial)==0) return i;
    }
    return -1;
}

//2byte文字を比較する.2byteが同じなら0を返す.違うなら0以外の数
int cmp2byte(char *str1,char *str2){
    if(str1[0]-str2[0] == 0){
        return str1[1]-str2[1];
    }
    return 1;
}
int get_number(char *word){
    int l=0;
    int i,j;

    for(i=0;word[i*2] != '\0';i++){
        if(l & 1<<i) continue;//過去に重複とされた文字なら次の文字へ
        for(j=i+1;word[j*2] != '\0';j++){
            if((l & 1<<j) == 0){//まだ過去に重複とされてない文字なら
                if(cmp2byte(word+i*2,word+j*2)==0){//比較して同じ文字なら
                    l |= 1<<i;
                    l |= 1<<j;
                    
                }
            }
        }
    }
    return l;
}
int main(){
    FILE *fp,*fp2;
    char word[WORD_MAX],filename[WORD_MAX];
    int m,n,l;//out-m-n-l.txt
    int i;
    if((fp = fopen("dict.txt", "r")) == NULL){    /* 入力ファイルオープン */
        printf("入力ファイルがオープンできません\n");
        exit(1);        /* 強制終了 */
    }

    m=0;
    for(;;){
        if(fgets(word,WORD_MAX,fp)== NULL) break;
        //文字数算出
        n = strlen(word);
        if(word[n-1] == '\n'){//改行削除
            word[n-1] = '\0';
            n--;
        }
        n /= 2;//2byte文字なので2で割る
        
        //頭文字でテーブル番号検索
        if((m = search_table(m,word)) < 0) break;
        
        //重複算出
        l=get_number(word);
        
        /*出力ファイル書き出し*/
        sprintf(filename,"out-%d-%d-%d.txt",m,n,l);
        if((fp2 = fopen(filename, "a")) == NULL){    /* 入力ファイルオープン */
            printf("出力ファイルがオープンできません\n");
            exit(1);/* 強制終了 */
        }
        fputs(word,fp2);
        fclose(fp2);

    }

    return 0;
}




No.14064

ありがとうございました。
投稿者---サミー(2004/05/16 18:48:31)


あかま様
大変勉強になります。恥ずかしいことに今までビットシフト演算子の存在を知りませんでした。
何故自分のプログラムが動かなかったかは疑問なので後で検証するとして、当面はこのプログラムを
参考にして課題に取り組みたいと思います。アドバイスありがとうございました。