サイトマップ / C言語講座出入り口総目次目次:ファイル>二分木探索(バイナリサーチ)||デモ用

青い直線

二分木探索(バイナリサーチ)

青い直線

[空白文字の出現頻度]←このソース→[バッファリング有り無し]

/* ソースプログラムの説明 */

/* まず最初にこのファイルを開いて下さい。そして、ファイル名を”Wtest.c”に変更しておいてください。今日は、Cのソースプログラムから、英単語を抜き出して、Cの予約語かどうか調べて、予約語だったらカウントするプログラムを作ります。

予約語とカウンタはセットになっている方が自然なので、構造体の配列を使います。構造体の配列は下に示すように型宣言をします。name[10] に予約語を入れ、countに数えた結果を入れます。

    typedef struct {
        char name[10];
        int  count;
    } KEYWORD;

構造体の配列を下記のように宣言します。予約語は、後に述べる 二分木探索に使うので、整列してあります。

    KEYWORD keyword[NUM_KW] = {
        {"break", 0}
        {"case", 0},
        - - - - - - -
        - - - - - - -
        {"while", 0}
    };

では、プログラムの流れについて、考えて見ましょう。

  1. Cソースファイルを読み込み専用で開く。
  2. 次に、予約語は英単語なので、このファイルから英単語を読み込む。
  3. この単語が予約語かどうか調べて、予約語ならカウントする。
  4. ファイルの終わりまで調べる。
  5. 結果を表示する。
  6. ファイルを閉じる。

ファイルから英単語を読み込む関数の名前を、GetWord( ) とします。この関数は引数として、ファイルポインタと、取り出した英単語をしまう文字列へのポインタを取ります。戻り値は成功したら読み込んだ文字数を、失敗したら0を返すようにします。

単語を取り出すには、ライブラリ関数、strtok( ) を使っても良いと思います。しかし、strtok( ) は1回目と2回目以降の呼び出しで、引数を変えなければならず、使いづらいので、今回は自作することにします。

英単語はアルファベットでできています。そこで、次のアルゴリズムで英単語を読み込みます。

    ファイルの先頭から1文字ずつ読み、

    もし、アルファベットでなければ、読み飛ばし、

    アルファベットなら、英単語をしまう配列に、その文字を書き込みます。

    それをアルファベットでない文字が出るまで続け、
    アルファベットでない文字が来たら、
    文字列の終末にヌル文字を追加して、読み込んだ文字数をリターンする。

アルファベットかどうかは、下記のライブラリ関数を使って調べます。

    #include  <ctype.h>
    int isalpha(char c);

    例:n = isalpha(c);

    実行結果                戻り値
    条件を満たせば          0以外の値
    条件を満たさなければ    0

ソースプログラムに詳しい説明を付けておきましたが、少し、補足します。int 型の変数 n に読み込んだ文字数を入れます。c = getc(fp) で、ファイルから c に1文字読み込みます。

if (!n && !isalpha(c)) が真になるのは、まだ1文字も読んでいなくて、かつ、c がアルファベットでない時です。この時は、読み飛ばします。if (n && !isalpha(c)) が真になるのは、文字を読み込んでいて、かつ、c がアルファベットでない時です。つまり、英単語を読み終わった時です。補足は、以上です。

次に、ファイルから読み込んだ単語が予約語かどうか調べて、予約語ならカウントする関数を作ります。この関数は、読み込んだ英単語をしまってある文字列へのポインタを取ります。戻り値は、予約語だった時は、見つけた位置 ( 後述 ) を返します。下記に関数の名前を示します。

    int BSearch(char *word);

予約語であるかどうかは、構造体の配列の中に、英単語と同じものがあるかどうかを、調べればわかります。

予約語の探索には、バイナリサーチ ( 二分木探索 ) という方法を使います。バイナリサーチを行うには、データが昇順に整列してあることが必要です。英単語の場合は、辞書のような順に並んでいるということです。

このアルゴリズムについて説明します。左から右に辞書順に予約語が並んでいるとします。

    英単語と、予約語の並びの中央のものと比較します。

    もし、一致したら予約語の位置を返して、終了します。

    もし、予約語の方が大きければ、次の探索対象は中央値の右側です。
    もし、予約語の方が小さければ、次の探索対象は中央値の左側です。

このように、順次探索の範囲をせばめていって、分割不能になったら、-1 を返して終了します。

ソースプログラムに詳しい説明をつけて置きましたので、あとはそちらをごらん下さい。探索結果を表示する関数は、簡単なのでソースプログラムをごらん下さい。 */

/* ここからソースプログラム */

#include <stdio.h>
#include <stdlib.h>    /* exit(  ) で必要 */
#include <string.h>    /* strcmp(  ) で必要 */
#include <ctype.h>     /* isalpha(  ) で必要 */

#define  NUM_KW  25    /* 25 の予約語を対象にする */

typedef struct {
    char name[10];      /* Cの予約語を入れる */
    int count;          /* カウンタ */
} KEYWORD;

  /* 構造体配列の宣言 */
KEYWORD keyword[NUM_KW] = { /* 二分木探索 ( バイナリサーチ ) に使うので */
    {"break", 0},           /* 構造体の最初の要素は整列してある */
    {"case", 0},
    {"char", 0},
    {"continue", 0},
    {"do", 0},
    {"double", 0},
    {"else", 0},
    {"enum", 0},
    {"extern", 0},
    {"float", 0},
    {"for", 0},
    {"if", 0},
    {"int", 0},
    {"long", 0},
    {"register", 0},
    {"return", 0},
    {"short", 0},
    {"static", 0},
    {"struct", 0},
    {"switch", 0},
    {"typedef", 0},
    {"union", 0},
    {"unsigned", 0},
    {"void", 0},
    {"while", 0}
};

int GetWord(FILE *fp, char *temp);
int BSearch(char *word);
void ShowResult(void);
void main(void);

  /* 英単語を temp[n] に読み込む */
int GetWord(FILE *fp, char *temp)
{
    int c;
    int n = 0;    /* 読み込んだ文字数 */
	
    while ((c = getc(fp)) != EOF) {
                                      /* 文字を 読み込んでいなくて */
        if (!n && !isalpha(c))        /* かつ、c はアルファベットでない */
            continue;                 /* 読み飛ばす */

        else if (n && !isalpha(c)) {  /* 英単語を読みおわったら */
            temp[n] = '\0';           /* 末尾にヌル文字を付けて */
            return (n);               /* 読み込んだ文字数を返す */
        }

        else if (isalpha(c)) {        /* アルファベットの時 */
            temp[n] = (char)c;        /* 英単語の読み込み */
            n++;
        }
    }
    return (0);                       /* キーワードの読み込みに失敗 */
}

  /* 二分木探索 ( バイナリサーチ ) を行って、 */
  /* 予約語が見つかったら、カウンタに1加える */
int BSearch(char *word)	
{
    int left = 0;
    int right = NUM_KW - 1;
    int mid;
    int x;
      /* 左から右へ辞書的に予約語が並んでいるとすると */
    while (left <= right) {
        mid = left + (right - left) / 2;	
        x = strcmp(keyword[mid].name, word);
        if (x > 0)                            /* 予約語は mid より左にある */
            right = mid -1;                   /* 左を探索 */
        else if (x < 0)                       /* 予約語は mid より右にある */
            left = mid + 1;                   /* 右を探索 */
        else {                                /* 見つかったら */
            keyword[mid].count += 1;          /* カウンタに1加えて、 */
            return (mid);                     /* リターンする */
        }
    }
    return (-1);
}

  /* 構造体の中身を表示 */
void ShowResult(void)
{
    int i;

    for (i = 0; i < NUM_KW ; i++) {
        printf("%10s %3d  ", keyword[i].name, keyword[i].count);
        if (!((i + 1) % 4))                  /* 予約語を4つ表示したら改行 */
            printf("\n");
    }
}

void main(void)
{
    FILE *fp;
    char word[100];

    if ((fp = fopen("wtest.c", "r")) == NULL) {        /* ファイルを開けなかったら */
        fprintf(stderr, "Can't Open C Source File!\n");/* メッセージを表示して */
        exit(2);                                       /* シェルへ戻る */
    }
      /* 英単語が見つかったら予約語かどうか調べる */
    while (GetWord(fp, word)) {
        BSearch(word) ;
    }
    ShowResult(  ) ;
    fclose(fp);
}

/* ここまでソースプログラム */

/* このプログラムで開いたファイル "Wtest.c" は、このプログラムから日本語のコメントを削ったファイルです。 */

[空白文字の出現頻度]←このソース→[バッファリング有り無し]

青い直線

/* (C) 2000- YFプロ. All Rights Reserved. */    提供:C言語講座−それ自体コンパイルできる教材を使った講座です−

青い直線

サイトマップ / C言語講座出入り口総目次目次:ファイル>二分木探索(バイナリサーチ)||デモ用