C言語関係掲示板

過去ログ

No667 ファイル内の文字列を別のファイルにランダムで出力する

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

ファイル内の文字列を別のファイルにランダムで出力する
投稿者---きー(2003/06/16 18:27:04)


初めまして。
色々なサイトを探したのですがどうしても分からなかったので投稿させてください。

最初にあるテキストファイルにある英単語を単語ごとに区切って読み込んだあと、
格納した単語の順番をランダムに入れ替えます。
その後入れ替えた単語をファイルに出力するプログラムを作成しています。

以下は考えたプログラムですがどうしても最後の文字しか出力されません。
(例えばテキストファイルに「sakura sumire yuri」とあった場合「yuri yuri yuri」と出力されます)

どのような点が間違っているのでしょうか。
よろしければご指摘お願いします。

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

#define ON 1
#define OFF 0
#define MAXWORD 10
#define MAXWORDLEN 32 
#define MemoryErr(where)  fprintf(stderr,"Memory full in " #where "\n", where) 

int     NowMaxSize ;

int ReadOneWord( FILE *Infile,char *word )
{
        char c ;
        char StoreFlg ;  
        int i ;

        i = 0 ;
        StoreFlg = OFF ;
        while( (c = getc(Infile)) != EOF ) {
                if ( isalpha(c) || isdigit(c) ) {
                        StoreFlg = ON ; 
                        word[i++] = c ; 
                        if ( i >= MAXWORDLEN ) return 0 ;
                }
                else if ( StoreFlg ) {
                        word[i++] = '\0' ; 
                        return i ;
                }
        }
        return EOF ;
}

int ReadWord( FILE *Infile,FILE *Outfile )
{
        int j ,k,p=0;
        char **word, *a[1000]; 
        char oneword[MAXWORDLEN] ; 
        int  wordlen, wordnum = 0 ;
        
        if ( (word = (char **) calloc( MAXWORD, sizeof( char *) ) ) == NULL ) {
                MemoryErr(0) ; 
                exit(-1) ;
        } 

        NowMaxSize = MAXWORD ;

        while ( (wordlen = ReadOneWord( Infile,oneword ) ) != EOF ) {
                if ( !wordlen ) {
                        fprintf(stderr, "Delimiters are not found...\n") ;
                }
                else {
                        p = wordnum;                           /* 番号の格納 */
                        a[p] = oneword;                        /* 文字列の格納 */
				if ( wordnum >= NowMaxSize ) { 
					NowMaxSize += MAXWORD ;
					if ( (word = (char **) realloc( word,  NowMaxSize*sizeof( char *) ) )
							== NULL ) {
						MemoryErr(3) ; 
						exit(-1) ;
					}
				}
				if ( (word[wordnum] = (char *) calloc( wordlen, sizeof( char ) ) ) == NULL ) {
					MemoryErr(5) ; 
					exit(-1) ;
				}
                                strcpy( word[wordnum++], oneword ) ;
                 }
	}
	for(j = 0;j<=p;j++){
          srand(p);                           /* 乱数を設定し */
          k = rand() % p;
          fprintf(Outfile,"%s\n",a[k]);       /* ファイルに出力する */
	}
          printf( " End of file \n" ) ;
}

main(int argc, char *argv[] ) /* 79 */
{
        int c  ;
        FILE *Infile,*Outfile ;

        if( argc != 3 ) {
                fprintf(stderr,"Usage: kadai2_3 inputfile\n") ; 
                exit(0) ;
        }
        
        if( (Infile = fopen( argv[1], "r" ) ) == NULL ) {
                fprintf(stderr,"Can not open %s\n", argv[1]) ; 
                exit(0) ;
        }
        if( (Outfile = fopen( argv[2], "w" ) ) == NULL ) {
                fprintf(stderr,"Can not open %s\n", argv[2]) ; 
                exit(0) ;
        }

        ReadWord ( Infile,Outfile) ;

        fclose( Infile ) ;
        fclose( Outfile) ;
}


No.7443

Re:ファイル内の文字列を別のファイルにランダムで出力する
投稿者---YuO(2003/06/16 19:38:07)


>最初にあるテキストファイルにある英単語を単語ごとに区切って読み込んだあと、
>格納した単語の順番をランダムに入れ替えます。
>その後入れ替えた単語をファイルに出力するプログラムを作成しています。
>以下は考えたプログラムですがどうしても最後の文字しか出力されません。
>(例えばテキストファイルに「sakura sumire yuri」とあった場合「yuri yuri yuri」と出力されます)
>どのような点が間違っているのでしょうか。

srandの使い方です。

大抵の処理系において,rand関数は,
Rn = (A * Rn-1 + B) % C
というような演算をして得られるRnから,
例えば上位15ビットを取得する,のようなことをやって返しています。
そして,srandの引数はそのままRn-1の値になります。

ここで,きーさんの書かれたプログラムでは,
forの中でsrandを同じ値で何度も呼び出しています。
上記式中でA, B, Cの値は定数なので不変ですから,
srandに同じ値を与えればrandの戻り値も当然等しくなります。


でもって,解決策。
main関数の先頭でsrandを呼び出すのがよいでしょう。
普通は
srand(time(NULL));
とやります。そして,forループ中からsrandの呼び出しを削除してください。


それから……。

        char c ;
        while( (c = getc(Infile)) != EOF ) {


ありがちな間違いですが……。
getcの戻り値はint型です。
最悪,無限ループになります。

これは,getcが返す可能性のある値が0x00 〜 0xFFであるため,
エラーはそれ以外の値を返さなければいけないからです。
で,無限ループになるというのは,StoreFlgがOFFの状態で,EOFを迎えたときに,
・charがunsigned charと等しい処理系
・EOFが負の値(典型例は-1)
の二つを満たす処理系では,!=の左辺が負になることはないので,ループが終了しないのです。

というわけで,cの型はintにする必要があります。


main(int argc, char *argv[] ) /* 79 */


将来のことを考えて,戻り値の型(int又はコンパイラが許可する型)を記述した方がよいです。
現行のISO Cでは認められなくなっています。


No.7448

Re:ファイル内の文字列を別のファイルにランダムで出力する
投稿者---きー(2003/06/17 08:36:32)


YuOさん、詳しい説明ありがとうございます。
返事遅くなり申し訳ありませんでした。

さっそく修正に当たりましたが、どうにも変わらないです。
ReadWord関数内の
for(j = 0;j<=p;j++){
          srand(p);                           /* 乱数を設定し */
          k = rand() % p;
          fprintf(Outfile,"%s\n",a[k]);       /* ファイルに出力する */
	}

の内容なのですが、例えば
for(j = 0;j<=p;j++){
          fprintf(Outfile,"%s\n",a[j]);
	}

といった単純記述でも同様の結果がでてしまいます。
whileのelse内に上記のfor文(乱数ではない方です)をコメントすると、
普通に出力されるのですが・・・

もしよろしければ、また解答よろしくお願いします。

No.7450

Re:ファイル内の文字列を別のファイルにランダムで出力する
投稿者---こん!(2003/06/17 08:42:16)


for(j = 0;j<=p;j++){
          fprintf(Outfile,"%s\n",a[j]);
    }
これ
for(j = 0;j<=p;j++){
          fprintf(Outfile,"%s\n",word[j]);
    }
じゃないの?

No.7456

Re:ファイル内の文字列を別のファイルにランダムで出力する
投稿者---きー(2003/06/17 12:34:44)


こん!さん、レスありがとうございます。

><pre>for(j = 0;j<=p;j++){
fprintf(Outfile,"%s\n",a[j]);
}
</pre>これ
><pre>for(j = 0;j<=p;j++){
fprintf(Outfile,"%s\n",word[j]);
}
</pre>じゃないの?

その通りでした!
これを実行したら順序通りに出力されました。
またYuOさんのやり方と組み合わせてみるとランダムに出力されました!

ということは

a[p] = oneword; /* 文字列の格納 */

の部分は不要だったのですね。
勉強になりました。
YuOdさん、こん!さん、そして名前のない方さんもありがとうございました。
アルゴリズムはまた自分で考え直したいと思います。

No.7457

Re:ファイル内の文字列を別のファイルにランダムで出力する
投稿者---こん!(2003/06/17 15:01:10)


>ということは
>
>a[p] = oneword; /* 文字列の格納 */
>
>の部分は不要だったのですね。

これが何のために有るのかも疑問ではあったのですが。

これはご自分で書かれたリストですか?もしそうだとするとコメントの内容に大
きな誤解がありますよ。ポインタの扱いを勉強しなおされた方がよろしいかと思
います。

ふざけているのではなく真面目に・・・

No.7451

Re:ファイル内の文字列を別のファイルにランダムで出力する
投稿者--- (2003/06/17 10:15:55)


1.srand() 関数は、乱数の種の設定で、
一番はじめに一度だけ実行すれば十分です。
また、「srand( time(NULL) );」と言う感じの方が一般的でしょう。

2.「ReadWord()」と言う関数名で、
読み込みと書き込みの両方を行ってしまう、
と言う設計はいかがなものか。
これは、入力と出力に分けて設計するべきでしょう。

いきなりコードを書かずに、
まずは日本語で「どういう処理を行うか」(アルゴリズム)、
を詳しく順を追って考えてみてはいかがでしょう?