C言語関係掲示板

過去ログ

No.132.単語の出現回数を求め、一覧を辞書順に出力


No.774

文字列について
投稿者---yusuke(2002/01/10 22:55:21)


始めまして。大学でCを学んでいる者です。どうしてもわからないところがあったので、質問させて頂きます。                       100単語以上の英文テキストを読み込み、その中に出てくる単語の出現回数を求め、その一覧を辞書順に出力するという問題なのですが、どうかご指導お願いします。(単語はスペース、ピリオド、コンマ、タブ、改行文字のいずれかで区切られています。)                       


No.776

Re:文字列について
投稿者---kikk(2002/01/11 01:02:51)


ども。

参考までに。
K&Rの自己参照構造体のところでほとんど同じ問題が取り上げられています。
2分木で実装しています。単語の区切り方が違うようですが。

では。

No.779

Re:文字列について
投稿者---yusuke(2002/01/11 10:18:42)


>ども。
>
>参考までに。
>K&Rの自己参照構造体のところでほとんど同じ問題が取り上げられています。
>2分木で実装しています。単語の区切り方が違うようですが。
>
>では。


アドバイスありがとうございます。早速探してみます。K&Rはたいていの本屋には売っていますか?実はまだ見たことが無くて・・・。すいません。

No.781

Re:文字列について
投稿者---shu(2002/01/11 11:10:21)



>アドバイスありがとうございます。早速探してみます。K&Rはたいていの本屋に>は売っていますか?実はまだ見たことが無くて・・・。すいません。


学校の図書館とかにないでしょうか?


No.780

Re:文字列について
投稿者---B.Smith(2002/01/11 11:10:19)


(時間的に手遅れかもしれませんが...)
私も少し試してみましたので、ご紹介しておきます。参考にしてみてください。
少し長めなので、ポイントだけ説明します。ソースを解読してみてください。
このソースはLSI-Cの試食版、MSC8(Visual C++ 1.0)、MSC12(Visual C++ 6.0)、Borland C3.1、Borland C5.5で動作確認しています。

切り出したトークンのポインタを構造体の配列に格納し、単語数のカウント、ソートを行います。
ライブラリリファレンスでは、関数strtokは「トークンの切り出しを行うと、バッファの内容を破壊する」とありますが、実際には「指定した区切り文字以外の場所にあったデータ」は残っています。これは、トークン切り出し処理が、区切り文字をヌルに置き換えていくというものであるからです。
This is a pen.

     ↓         ↓     ↓   ↓
     T h i s \0 i s \0 a \0 p e n \0

切り出したトークンの先頭位置さえ保存しておけば、後で参照することが可能なのです。
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define     SEPARATOR   " .,\t\r\n"

typedef int     (*COMP )(const void *,const void *);

typedef struct  {
    char    *pWord;
    int     Count;
} WORDINFO,*PWORDINFO;

/* とりあえず単語種類を200とする */
WORDINFO        WordTbl[200];

int     Compare(PWORDINFO ,PWORDINFO );

void main(void )
{
    PWORDINFO   pSearch,pTbl;
    char        *pText,*tk;
    size_t      uSize;
    size_t      TblLen;
    FILE        *fp;

/*** phase 1 テキストをメモリにロード ***/
    fp = fopen("Test.txt","r");
    if (!fp)    return;

    uSize = (size_t )filelength(fileno(fp));
    pText = (char *)calloc(uSize + 1,sizeof(char ));
    if (!pText){
        fclose(fp);
        return;
    }
    fread(pText,uSize,1,fp);
    fclose(fp);

/*** phase 2 単語の位置をテーブルに格納する ***/
    tk = strtok(pText,SEPARATOR);
    while(tk){
        pSearch = WordTbl;
        while(pSearch->pWord){
            if (!strcmp(pSearch->pWord,tk))
                break;
            pSearch++;
        }
        pSearch->pWord = tk;
        pSearch->Count++;

        tk = strtok(NULL,SEPARATOR);
    }

/*** phase 3 ソート ***/
    pTbl = WordTbl; while((pTbl++)->pWord);
    TblLen = pTbl - WordTbl - 1;

    qsort(WordTbl,TblLen,sizeof(WORDINFO ),(COMP )Compare);

    pTbl = WordTbl;
    while(pTbl->pWord){
        printf("%s  .....  %d\n",pTbl->pWord,pTbl->Count);
        pTbl++;
    }

    /* 領域pTextは最後まで解放してはならない */
    free(pText);
}

int     Compare(PWORDINFO ptr1,PWORDINFO ptr2)
{
    return strcmp(ptr1->pWord,ptr2->pWord);
}





No.788

Re:文字列について
投稿者---yusuke(2002/01/11 17:46:12)



どうもありがとうございます。ところでこれ↑は、コピーして、C++にそのまま貼り付ければできるんでしょうか?また、例えば「test.txt」というファイルに予め格納されている英文を読み込んで、処理するときはどうすればいいんでしょうか。初歩的な質問ですいません。宜しくお願いします。


No.790

Re:文字列について
投稿者---B.Smith(2002/01/11 18:57:22)


>ところでこれ↑は、コピーして、C++にそのまま貼り付ければできるんでしょうか?

・「このプログラムをそのまま使えるのか」ということでしたら、問題なく動作します。C++コンパイラでもコンパイルできます。

>例えば「test.txt」というファイルに予め格納されている英文を読み込んで、処理するときはどうすればいいんでしょうか。

・このプログラムは、ファイルオープン時に"Test.txt"と、定数でファイル名を与えていますので、実行するとファイル"Test.txt"を即読みに行きます。そのため、実行する前にファイルTest.txtを用意しておかなければなりません。
コンパイル後の実行ファイルと同じ場所に"Test.txt"を置いておけば大丈夫です(カレントディレクトリ)。

No.791

Re:文字列について
投稿者---yusuke(2002/01/11 19:57:27)



>・このプログラムは、ファイルオープン時に"Test.txt"と、定数でファイル名を与えていますので、実行するとファイル"Test.txt"を即読みに行きます。そのため、実行する前にファイルTest.txtを用意しておかなければなりません。
>コンパイル後の実行ファイルと同じ場所に"Test.txt"を置いておけば大丈夫です(カレントディレクトリ)。

Test.txtを作って、アドバイスしてくださったプログラムを実行しようとすると、次のようなエラーメッセージが出てしまいます。コンパイルは通るのですが・・・。>コンパイル後の実行ファイルと同じ場所に"Test.txt"を置いておけば・・・とありますが、そこのところ詳しく教えて頂けないでしょうか。
--------------------構成: rep1 - Win32 Debug--------------------
リンク中...
rep3.obj : error LNK2005: _main はすでに rep1.obj で定義されています
Debug/rep1.exe : fatal error LNK1169: 1 つ以上の複数回定義されているシンボルが見つかりました
link.exe の実行エラー

rep1.exe - エラー 2、警告 0


No.796

Re:文字列について
投稿者---B.Smith(2002/01/11 20:54:13)


分かりました。mainがすでに存在するのですね。
関数化したソースを載せますのでコンパイル、リンクしてください。使用されているコンパイラは恐らくVisual C++だと思いますので、別ファイルとしてプロジェクトに取り込んだ方が今後の管理が容易になります。
使用する際はmain内から関数WordSearchを呼び出してください。例えば、
#include <stdio.h>

int     WordSearch(char *);

void main(void )
{
    if (WordSearch("Test.txt"))
        printf("Success\n");
    else
        printf("Fail\n");
}

この関数の引数にファイル名を指定します。ファイルが開けなかったり、メモリが確保できない場合は0を返し、成功すれば1を返します。
関数化したソース
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define     SEPARATOR   " .,\t\r\n"

typedef int     (*COMP )(const void *,const void *);

typedef struct  {
    char    *pWord;
    int     Count;
} WORDINFO,*PWORDINFO;

/* とりあえず単語種類を200とする */
WORDINFO        WordTbl[200];

int     Compare(PWORDINFO ,PWORDINFO );

int     WordSearch(char *FileName)
{
    PWORDINFO   pSearch,pTbl;
    char        *pText,*tk;
    size_t      uSize;
    size_t      TblLen;
    FILE        *fp;

/*** phase 1 テキストをメモリにロード ***/
    fp = fopen(FileName,"r");
    if (!fp)    
        return 0;

    uSize = (size_t )filelength(fileno(fp));
    pText = (char *)calloc(uSize + 1,sizeof(char ));
    if (!pText){
        fclose(fp);
        return 0;
    }
    fread(pText,uSize,1,fp);
    fclose(fp);

/*** phase 2 単語の位置をテーブルに格納する ***/
    tk = strtok(pText,SEPARATOR);
    while(tk){
        pSearch = WordTbl;
        while(pSearch->pWord){
            if (!strcmp(pSearch->pWord,tk))
                break;
            pSearch++;
        }
        pSearch->pWord = tk;
        pSearch->Count++;

        tk = strtok(NULL,SEPARATOR);
    }

/*** phase 3 ソート ***/
    pTbl = WordTbl; while((pTbl++)->pWord);
    TblLen = pTbl - WordTbl - 1;

    qsort(WordTbl,TblLen,sizeof(WORDINFO ),(COMP )Compare);

    pTbl = WordTbl;
    while(pTbl->pWord){
        printf("%s  .....  %d\n",pTbl->pWord,pTbl->Count);
        pTbl++;
    }

    /* 領域pTextは最後まで解放してはならない */
    free(pText);

    return 1;
}

int     Compare(PWORDINFO ptr1,PWORDINFO ptr2)
{
    return strcmp(ptr1->pWord,ptr2->pWord);
}

データであるテキストファイルはプロジェクトフォルダ内に置いてください(ソースが置かれている場所です)。
今回は関数化したので、ファイル名は呼び出し側(main側)で任意に決定できます。




No.797

Re:文字列について
投稿者---yusuke(2002/01/11 21:16:36)


何度もありがとうございます。早速やってみます。

No.798

Re:文字列について
投稿者---yusuke(2002/01/11 21:51:24)


何とかできました。ありがとうございます。
何度もあつかましくて申し訳ないのですが、大文字を小文字とみなすやり方を、先ほどのソースに挿入して教えて頂けないでしょうか?tolower()関数を使えばいいことはわかっているのですが、どこでどのように使えばいいのかよくわからなくて・・・。
あと、単語出現回数の一覧を出力して、その一覧をそのまま別のファイルに格納するにはどのようにすればよいのでしょうか。
ご指導お願いします。

No.800

Re:文字列について
投稿者---B.Smith(2002/01/11 23:37:36)


(大文字を小文字に変換)
/*** phase 2 単語の位置をテーブルに格納する ***/
    tk = strtok(pText,SEPARATOR);
    while(tk){

        *tk = tolower(*tk); /* 頭文字を小文字に変換 */

        pSearch = WordTbl;
        while(pSearch->pWord){
            if (!strcmpi(pSearch->pWord,tk))
                break;
            pSearch++;
        }
        pSearch->pWord = tk;
        pSearch->Count++;

        tk = strtok(NULL,SEPARATOR);
    }

このタイミングで変換すれば、すべての単語の頭文字が小文字になります。これは、バッファの内容を変更してしまっているので、表示も小文字になります。

頭文字にだけでなく、単語全体に対して比較の区別をしたくない場合は、関数strcmpiがあります。この関数はstrcmpとほぼ同じなのですが、大文字・小文字の区別はしません。以下の単語はすべて同一のものと見なされます。
This   this  tHiS  thiS  …

この関数を使用したい場合は、プログラム中で使用されている2箇所の関数strcmpをstrcmpiに置き換えるだけです(上記の場所と、qsortのコールバック関数内の2箇所)。

(結果をファイルにする)
恐らくコンソールアプリケーションでしょうから、DOSプロンプトを開いて、コマンドラインから、
      rep1 > Result.txt

と入力すれば、結果はテキストファイルに記録されます。
プログラムでテキストファイルを作成したい場合は、以下のようにします。
/*** phase 3 ソート ***/
    pTbl = WordTbl; while((pTbl++)->pWord);
    TblLen = pTbl - WordTbl - 1;

    qsort(WordTbl,TblLen,sizeof(WORDINFO ),(COMP )Compare);

    /* 結果を出力するファイルをオープン */
    fp = fopen("Result.txt","w");
    if (!fp)
        return 0;

    pTbl = WordTbl;
    while(pTbl->pWord){
        printf("%s  .....  %d\n",pTbl->pWord,pTbl->Count);

        /* ファイルに出力する */
        fprintf(fp,"%s  .....  %d\n",pTbl->pWord,pTbl->Count);
        pTbl++;
    }

    /* ファイルのクローズ */
    fclose(fp);

    /* 領域pTextは最後まで解放してはならない */
    free(pText);

    return 1;
}

・ソート後、出力するテキストファイルを開きます。ファイルポインタは、読み込みの時に使用したものを使いまわします。
・関数fprintfを追加します。
・ループ終了後、ファイルをクローズします。



No.806

Re:文字列について
投稿者---yusuke(2002/01/12 00:44:49)


本当にありがとうございました。大変勉強になりました。またお世話になるかもしれませんが、そのときは懲りずにご指導お願いします。

No.808

Re:文字列について
投稿者---yusuke(2002/01/12 00:53:05)


すいません!最後にもう一つ教えてください!
先ほどのプログラムでは、例えば Do you play tennis? を判定するとき、tennis に?がくっついてしまいます。tennis? という単語であると認識されるわけです。コンマ、ピリオド、びっくりマークも同じように文字の一つであると認識されてしまうのです。こうならないようにするにはどこでどうすればよいのでしょうか?宜しくお願いします。

No.810

Re:文字列について
投稿者---yusuke(2002/01/12 10:16:29)


ちなみにこれ、UNIXでも正常に動きますか?




No.811

Re:文字列について
投稿者---Qlock(2002/01/12 11:39:38)


>ちなみにこれ、UNIXでも正常に動きますか?
それくらい自分で試してみれば解ることでしょうに・・・
人に聞くのは悪いことではないですが、自分でよく考えてからにしたほうが
いいですよ。あなたの質問は本を見ればどこにでも掲載されているものばかり
なのですから。
少しキツくなってしまいましたが、悪い意味でとらないでくださいね



No.813

Re:文字列について
投稿者---yusuke(2002/01/12 11:47:32)


>>ちなみにこれ、UNIXでも正常に動きますか?
>それくらい自分で試してみれば解ることでしょうに・・・
>人に聞くのは悪いことではないですが、自分でよく考えてからにしたほうが
>いいですよ。あなたの質問は本を見ればどこにでも掲載されているものばかり
>なのですから。
>少しキツくなってしまいましたが、悪い意味でとらないでくださいね
>

すいません・・。
学校や近くの本屋を探してみたのですが、K&Rすら売っていなくて・・。
勉強して出直してきます。


No.824

Re:文字列について
投稿者---2児のオヤジ です。(2002/01/12 19:07:30)




>学校や近くの本屋を探してみたのですが、K&Rすら売っていなくて・・。

ねっとで発注するのが、良い方法と思います。

https://www.honya-town.co.jp/index.html

とか、、、見てみて下さい。




No.812

Re:文字列について
投稿者---B.Smith(2002/01/12 11:41:07)


おはようございます。

最も簡単な方法は、消去したい文字を単語の区切り文字として扱うことです。
現在、区切り文字を定義している部分に、消去したい文字を追加します。
#define     SEPARATOR   " .,\t\r\n?!"


UNIXで動作するかは確認していませんが、多分大丈夫ではないかと思います(多分ですけど)。また、それほど難しいことはしていないので、移植作業が必要であっても、特に問題ないと思います。

No.814

Re:文字列について
投稿者---yusuke(2002/01/12 11:53:36)


>おはようございます。
>
>最も簡単な方法は、消去したい文字を単語の区切り文字として扱うことです。
>現在、区切り文字を定義している部分に、消去したい文字を追加します。
>
#define     SEPARATOR   " .,\t\r\n?!"

>
>UNIXで動作するかは確認していませんが、多分大丈夫ではないかと思います(多分ですけど)。また、それほど難しいことはしていないので、移植作業が必要であっても、特に問題ないと思います。

おはようございます。
お返事ありがとうございます。
今UNIXでやってみているのですが、<io.h>のインクルードファイルが見つからないとのエラーメッセージ
が出てしまいました。これは一体どういうことをいっているのでしょうか?


No.816

Re:文字列について
投稿者---B.Smith(2002/01/12 13:14:21)


私はUNIXは分からないので、確かなことは言えませんが、io.hが無いということなので、恐らく低水準入出力をサポートしていないのでしょう。
このプログラムでは、io.hは関数filelengthのために必要です。そのため、ファイルサイズを別の方法で取得しなければなりません。
    fpos_t      StartPos,EndPos;

    /** ファイルサイズを取得する    **/
    /** 終了位置をそのままファイルサイズとしても良いのだが、安  **/
    /** 全のため先頭位置から終了位置までの差をファイルサイズと  **/
    /** する                                                **/
    fgetpos(fp,&StartPos);  /* 先頭位置 */
    fseek(fp,0,SEEK_END);
    fgetpos(fp,&EndPos);    /* 終了位置 */
    rewind(fp);             /* 読み込みのため、先頭に戻す */

    uSize = (size_t )(EndPos - StartPos);

ここで使われている関数はANSIなので、UNIXでも多分大丈夫だと思います。ファイルポインタの位置からファイルサイズを取得します。

Qlockさんの、

>人に聞くのは悪いことではないですが、自分でよく考えてからにしたほうが
>いいですよ。

これは、まったくその通りです。もちろん、誰にも聞かずに分からないままにするよりは、遥かに良いことなのですが、業務では自分で調べる事がかなり重要になってきます。これは、学業の場合と違って、実際に使われる技術に限りが無いためです。会社にとって新しいことは「人に聞いても分からない」という状況も出てきます。

深く考える必要はありませんが、先生や調べる機材がある、という恵まれた環境の中にいるうちに、少しずつ、調査、実験をする方法を身につけて行ってください。調べ方も技術の内ですよ?




No.817

Re:文字列について
投稿者---yusuke(2002/01/12 13:41:48)


ありがとうございました。
もっと勉強してきます。



No.830

ファイルサイズ取得
投稿者---kikk(2002/01/13 02:24:20)


ども。


>
    fpos_t      StartPos,EndPos;

    /** ファイルサイズを取得する    **/
    /** 終了位置をそのままファイルサイズとしても良いのだが、安  **/
    /** 全のため先頭位置から終了位置までの差をファイルサイズと  **/
    /** する                                                **/
    fgetpos(fp,&StartPos);  /* 先頭位置 */
    fseek(fp,0,SEEK_END);
    fgetpos(fp,&EndPos);    /* 終了位置 */
    rewind(fp);             /* 読み込みのため、先頭に戻す */

    uSize = (size_t )(EndPos - StartPos);


fgetpos()では、fpos_t型の変数にファイルポインタの位置情報を
格納はしますが、そのまま数値として格納することまでは規定されて
いません(そういう実装がされている可能性はありますが)。したがって
使い方が間違っていると思います。規格の範囲でやるなら以下のような
感じかと。サイズがlongで表現できない場合(2G以上)に破綻しますけど
(もしかしたらそれを避けるために上記のような実装にしたのかも、と
ふと思いました)。。
/* ついでに関数化 */
long my_filelength(FILE *fp) {
  long len;
  fpos_t current_pos;

  fgetpos(fp,¤t_pos);  /* ファイルポインタ退避 */
  fseek(fp,0,SEEK_END);  /* ポインタを最後に */
  len=ftell(fp);  /* ポインタ位置取得 */
  fsetpos(fp,¤t_pos);  /* ファイルポインタ復帰 */

  return (len);
}

上記のコードの問題点としては、fpがテキストモードで開かれていた場合、
(改行コードがCRLFだと)実際のファイルサイズとは異なったものになる
という点があります。今回のような使い方なら問題になりませんが。

なお、ファイルサイズの取得に関してはC FAQにも記述があります(多分)。


では。

No.829

Re:文字列について
投稿者---kikk(2002/01/13 02:24:05)


ども。


参考までに。

非標準なのはfileno()とfilelength()とstrcmpi()です。
このうちfilelength()はUNIXには多分ありません。sys/stat.hの
stat()ないしfstat()で、(他の情報も含んだ構造体のメンバとして)
取得できます(非標準ですがDOS/WinでもOK)。また、strcmpi()は
同じ機能のstricmp()というのもありますが、こちらのほうが
やや移植性が高いようです。

移植に際しては、とりあえずコンパイルしてみて、エラーがでたら
無い関数を(UNIXならmanで)調べてヘッダファイルを探すとよいかと。
それでもダメな場合は同等の関数を探すか、自分で作るしかないです。


では。

No.831

Re:文字列について
投稿者---yusuke(2002/01/13 13:47:35)


>ども。
>
>
>参考までに。
>
>非標準なのはfileno()とfilelength()とstrcmpi()です。
>このうちfilelength()はUNIXには多分ありません。sys/stat.hの
>stat()ないしfstat()で、(他の情報も含んだ構造体のメンバとして)
>取得できます(非標準ですがDOS/WinでもOK)。また、strcmpi()は
>同じ機能のstricmp()というのもありますが、こちらのほうが
>やや移植性が高いようです。
>
>移植に際しては、とりあえずコンパイルしてみて、エラーがでたら
>無い関数を(UNIXならmanで)調べてヘッダファイルを探すとよいかと。
>それでもダメな場合は同等の関数を探すか、自分で作るしかないです。
>
>
>では。

どうもありがとうがざいます。たしか、filelength()を使うためにヘッダーファイルを<io.h>にしていると聞きましたが、これは、<stat.h>でインクルードして、fstat()を使えば同じ働きができるということですか?

No.832

Re:文字列について
投稿者---kikk(2002/01/14 02:37:41)


ども。


>>このうちfilelength()はUNIXには多分ありません。sys/stat.hの
>>stat()ないしfstat()で、(他の情報も含んだ構造体のメンバとして)

>どうもありがとうがざいます。たしか、filelength()を使うためにヘッダーファイルを<io.h>にしていると聞きましたが、これは、<stat.h>でインクルードして、fstat()を使えば同じ働きができるということですか?

ファイルサイズの取得という目的が達成できるという意味では「同じ」
ですが、それぞれの機能は違ったものです。また、置き換えるだけで
よいというものでもありません(上記引用参照)。それと、<sys/stat.h>
です(上記引用参照)。fstat()の詳細はコンパイラのライブラリリファレンス
(UNIXならmanが簡単ですね)を参照してください。


では。

No.833

Re:文字列について
投稿者---yusuke(2002/01/14 02:44:39)


>ども。
>
>
>>>このうちfilelength()はUNIXには多分ありません。sys/stat.hの
>>>stat()ないしfstat()で、(他の情報も含んだ構造体のメンバとして)
>
>>どうもありがとうがざいます。たしか、filelength()を使うためにヘッダーファイルを<io.h>にしていると聞きましたが、これは、<stat.h>でインクルードして、fstat()を使えば同じ働きができるということですか?
>
>ファイルサイズの取得という目的が達成できるという意味では「同じ」
>ですが、それぞれの機能は違ったものです。また、置き換えるだけで
>よいというものでもありません(上記引用参照)。それと、<sys/stat.h>
>です(上記引用参照)。fstat()の詳細はコンパイラのライブラリリファレンス
>(UNIXならmanが簡単ですね)を参照してください。
>
>
>では。

夜遅くにありがとうございます。
早速頑張ってみます。

No.845

プログラムを繋げる
投稿者---yusuke(2002/01/16 12:53:05)


どうも。先日質問をさせていただいたyusukeです。
あれから一生懸命勉強して、自分なりにやってみました。
みなさんのおかげで、なんとか文字列を処理するプログラムはできたのですが、
「プログラムを繋げる」というところでつまずいてしまいました。
色々と調べてみましたが、どうしてもうまくいきません。
どなたか教えてくれませんか?

1.100単語以上の英文を読み取り、それをfile1に格納する。
2.file1から英文を読み取り、単語の出現回数をもとめ、その一覧(出現順)をfile2に
格納する。
3.2とは違い、一覧を辞書順に並べ変えてfile3に格納する。
4.file1, file2, file3をそれぞれ標準出力に書き出す。
これら1から4を順に実行するプログラ厶をつくる。

まず、それぞれのプログラムを4つ別々につくって、あとでそれら一つ一つに
名前を付け、(もともとmain()だった部分をfirst(),second()などにして関数化する)最後にmain()で実行しようとしたのですが、
3がどうしてもうまくいかないのです。(インクルード等は一番最初にまとめてあります。)
2と3は「文字列について」でB.Smithさんにつくっていただいたものを
ベースにしていますので、そちらをみていただけるとわかるかと思います。2と3の違いは
ソートがあるかないかだけです。2はうまく作動するのですが、(main()をsecond()にして)
3だけが・・・。main()をthird()にしてはいけないのでしょうか?







No.856

Re:プログラムを繋げる
投稿者---B.Smith(2002/01/17 17:58:07)


どのようにうまくいかないのかが分かりかねるため、想像でお答えします。違っていたら指摘してください。

もし、No.780のソースをベースにしているのであれば、単純に処理を関数毎に分断しただけではだめです。テキストデータを格納するバッファは、単語の検索後も存在しなければなりません。構造体内に、そのバッファのアドレスを格納しているからです。
関数secondで単語を検索し、関数終了時にバッファを解放してしまうと、そのポインタの指す場所が不定なため、正しくソートすることはできません(当然、表示も正しくできません)。

今回のように、各処理を関数化した場合、

・テキストを格納するバッファをグローバルで確保するか、テキストの読み込み・検索・ソート・表示が終了するまでの間、バッファを解放しない。
作成したプログラムにそれほど手を加える必要がないので、簡単ではあるのですが、作成したファイルは、実行時のメモリ状態をファイルとして残しているため、「ファイルを生成したプログラムが実行中の時のみ有効なデータ」です。処理中のデータを、見て分かるデータとして残す、いわば「処理の証拠」が欲しい場合は、この方法は使えません。

・構造体を変更する。発見した単語をポインタとしてではなく、文字列として格納します。例えば、
typedef struct  {
    char    Word[256+1];    /* 一つの単語の長さ最大をを256文字とする */
    int     Count;
} WORDINFO;

構造体の内容が変更されるため、各処理に少し手を加えなければなりませんが、ファイル内のデータはプログラムが終了しても有効です(別のツールでも参照可能です)。



No.891

Re:プログラムを繋げる
投稿者---yusuke(2002/01/19 18:24:35)


なるほど。よくわかりました。
構造体等の宣言を一番最初にまとめてしまっていて、更新していなかったからなんですね。
ありがとうございました。

戻る


「初心者のためのポイント学習C言語」 Last modified:2002.02.03
Copyright(c) 2000-2002 TOMOJI All Rights Reserved