C言語関係掲示板

過去ログ

No717 過去ログNo132のソースについて

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

過去ログNo132のソースについて
投稿者---スライム(2003/07/26 18:17:57)


過去ログNo132
http://f1.aaa.livedoor.jp/~pointc/log132.html
の中に書いてある下記のソースで意味が理解できない点があるので
教えてください。


#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);
}



このソースの中の下記の意味が理解できないので教えてください。
お願いします。
typedef int (*COMP )(const void *,const void *);
} WORDINFO,*PWORDINFO;PWORDINFO pSearch,pTbl;
char *pText,*tk;
size_t uSize;
size_t TblLen;
uSize = (size_t )filelength(fileno(fp));







No.8656

Re:過去ログNo132のソースについて
投稿者---YuO(2003/07/26 19:10:54)


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

『「const void *型の引数を二つとりintを返す関数」へのポインタ』という型に,
COMPという名前を付けています。


>(2) } WORDINFO,*PWORDINFO;PWORDINFO pSearch,pTbl;
> char *pText,*tk;
> size_t uSize;
> size_t TblLen;

typedef struct { /* ... */ } WORDINFO, * PWORDINFO;
について。これは,
struct tagWordInfo { /* ... */ };
typedef struct tagWordInfo WORDINFO;
typedef struct tagWordInfo * PWORDINFO;
と同じことで,
・この構造体型に,WORDINFOをいう名前を付ける
・この構造体へのポインタ型に,PWORDINFOという名前を付ける
ということです。

残りは,普通の宣言です。


>(4) uSize = (size_t )filelength(fileno(fp));

これは何がわからなかったのでしょう。
関数呼び出して,戻り値をsize_t型にキャストしています。


No.8659

Re:過去ログNo132のソースについて
投稿者---スライム(2003/07/26 23:32:52)


早速回答ありがとうございます。

size_t uSize;
size_t TblLen;
に関しては、size_t というのはどういった型なのかわからないので
教えてください。

filelengthの意味も調べたのですが、参考書などに載っていなくて
よくわからないんですが、これについてもお願いします。
filelengthはUNIXでは使えないのでしょうか?


No.8660

Re:過去ログNo132のソースについて
投稿者---YuO(2003/07/27 00:32:25)


> size_t uSize;
> size_t TblLen;
>に関しては、size_t というのはどういった型なのかわからないので
>教えてください。

sizeof演算子の結果の符号無し整数型,と定められています。
stddef.hを見れば載っているとおもいますが,
大抵の環境ではunsigned intのtypedefだと思います。


>filelengthの意味も調べたのですが、参考書などに載っていなくて
>よくわからないんですが、これについてもお願いします。
>filelengthはUNIXでは使えないのでしょうか?

元々,filelength自体が非標準の(VC++の)関数です。
No.829 "Re:文字列について" (kikkさん)
の記事をよく読んでみましょう。

No.8661

Re:過去ログNo132のソースについて
投稿者---スライム(2003/07/27 02:21:13)


>元々,filelength自体が非標準の(VC++の)関数です。
>No.829 "Re:文字列について" (kikkさん)
>の記事をよく読んでみましょう

読んでみたのですが、初心者の私には、中々理解しがたく
C++のソースを自分なりに、Cのプログラムで考えてみたんですが、

fileno()とfilelength()とstrcmpi()の代わりになるCの関数がわからなく、stat()、fstat()についても色々探してみたのですが、参考書が初心者用なので載っておらずわかりませんでした。

代わりになるCの関数がわからなかったので、fileno()とfilelength()とstrcmpi()を使用して、cのプログラムを考えて見たのですが、実際に、Cのプログラムでこのプログラムを動かすにはどのような関数を使えば動かすことが出来るのでしょうか。

よろしければ、このプログラムを修正して、教えてください。


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

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

typedef int
{
     (*COMP )(const void *,const void *);a
};

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

int     WordSearch(char *FileName);

void main(void )
{
    WordSearch(char *);
}

/***************************************************
		WORD毎に切り出す関数

この関数の引数にファイル名を指定します。ファイルが開けなかったり、
メモリが確保できない場合は0を返し、成功すれば1を返します。
******************************************************/
/* とりあえず単語種類を300とする */
WORDINFO        WordTbl[300];

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);/*size_t型のサイズ*1個をpTextに読み込む*/
	
    fclose(fp);

/*** phase 2 単語の位置をテーブルに格納する ***/
	/    tk = strtok(pText,SEPARATOR);/*SEPARATORに含まれる文字をを\nとしてpTextから文字列を取り出す。*/
    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);
    }

/*** 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);

    /* 領域解放 */
    free(pText);

    return 1;
}

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



No.8663

Re:過去ログNo132のソースについて
投稿者---かずま(2003/07/28 00:42:06)


質問に対する回答でなくて申し訳ありませんが、そのプログラムを見ていると、ファ
イルを全部読み込むという領域の無駄と、calloc でその領域を確保しているという
時間の無駄が気になって、同じようなことをするプログラムを別に書いてみました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main(void)
{
    struct Word { struct Word *next; char *word; int count; } list = {0}, *p, *w;
    char word[256], *s;  int n;  FILE *fp = stdin;

    for (; (n = fscanf(fp, " %255[a-zA-Z]", word)) != EOF; fscanf(fp, "%*[^a-zA-Z]")) {
        if (n == 0) continue;
        for (s = word; *s; s++) *s = tolower(*s & 0xFF);
        for (p = &list; p->next && (n = strcmp(p->next->word, word)) < 0; p = p->next)
            ;
        if (n == 0) { p->next->count++; continue; }
        if (!(w = malloc(sizeof *w)) || !(w->word = strdup(word)))
            return puts("out of memory"), 1;
        w->next = p->next,  p->next = w,  w->count = 1;
    }
    for (p = &list; p = p->next; ) printf("%5d %s\n", p->count, p->word);
    return 0;
}

fscanf の "%[" を使っているので、速くはなっていないでしょう。

No.8664

Re:過去ログNo132のソースについて
投稿者---あかま(2003/07/28 01:43:15)


ちょっとスレッドとは関係ないのですが質問です。

scanfの"*"を使った読み捨てなのですが、
"%*c"で一文字読み捨て、
"%*s"で次にスペース改行などが来るまでの文字列読み捨て、
"%*[^a-zA-Z]"で"a-zA-Z"以外の文字が続く限り0文字以上読み捨て
でよいでしょうか?

なにやらこの辺の知識が曖昧で困ってます。
とりあえず、上の知識でいいのなら、かずまさんのソースのfscanfをまとめてしまって
↓のようにしても大丈夫かなと思ったのですがどうでしょうか?
for (; (n = fscanf(fp, "%*[^a-zA-Z]%255[a-zA-Z]", word)) != EOF;)


No.8665

Re:過去ログNo132のソースについて
投稿者---かずま(2003/07/28 08:43:35)


> とりあえず、上の知識でいいのなら、かずまさんのソースのfscanfをまとめてしまって
> ↓のようにしても大丈夫かなと思ったのですがどうでしょうか?
> for (; (n = fscanf(fp, "%*[^a-zA-Z]%255[a-zA-Z]", word)) != EOF;)

そうですね。そのほうがいいですね。あるいは、while に書き換えた

    while ((n = fscanf(fp, "%*[^a-zA-Z]%255[a-zA-Z]", word)) != EOF)


なぜ、あのような fscanf を書いたかというと、最初、次のようなものを考えて
これではダメだと思ったからです。

    while ((n = fscanf(fp, " %255[a-zA-Z]%*[^a-zA-Z]", word)) != EOF)


No.8666

Re:過去ログNo132のソースについて
投稿者---あかま(2003/07/28 10:52:14)


while ((n = fscanf(fp, " %255[a-zA-Z]%*[^a-zA-Z]", word)) != EOF)

すみませぬ。これのダメなところがわからないのですが…
頭に"%255[a-zA-Z]"があるので、読み込んでしょっぱなに"[^a-zA-Z]"が来たら
読めなくなるのかなと思ったのですが、手元のbccでは問題なく動きます。
なにか他に理由がありますでしょうか?


No.8669

Re:過去ログNo132のソースについて
投稿者---かずま(2003/07/28 13:53:23)


> while ((n = fscanf(fp, " %255[a-zA-Z]%*[^a-zA-Z]", word)) != EOF)
> すみませぬ。これのダメなところがわからないのですが…
> 頭に"%255[a-zA-Z]"があるので、読み込んでしょっぱなに"[^a-zA-Z]"が来たら
> 読めなくなるのかなと思ったのですが、手元のbccでは問題なく動きます。
> なにか他に理由がありますでしょうか?

入力が
--- abc def abc ---
の場合、gcc や VC++ では無限ループになります。先頭の - が "%255[a-zA-Z]"
にマッチしないので、それを読まず、入力動作を打ち切ります。
bcc は、 "%255[a-zA-Z]" が失敗した後、"*[^a-zA-Z]" に進んで、--- を
読み飛ばすようです。次のプログラムを試しても分かります。
    char s1[100] = "ABCDE", s2[100] = "FGHIJ";
    int n = sscanf("--- abc def", " %[a-z]%s", s1, s2);
    printf("n=%d, s1=%s, s2=%s\n", n, s1, s2);

bcc の結果は、n=1, s1=ABCDE, s2=---
gcc と VC++ の結果は、n=0, s1=ABCDE, s2=FGHIJ
bcc は、失敗したら、その文字以降は読まないという規格に反しています。

No.8671

Re:過去ログNo132のソースについて
投稿者---あかま(2003/07/28 20:56:48)


コンパイラの規格無視ですか。
そういえばCを触り始めたころ戻り値でその後の処理を変える(エラー処理ではなく)プログラムを書いた憶えが。
コンパイラを変えると動かなかったのはこれが原因か!

非常に恐ろしいことをしていたのね…