単語をグループごとにソート表示

K&R本 演習6-2

Cのプログラムを読み込んで、最初の6文字が同じで、その後のどこかが異なっている変数名の各グループを、アルファベット順に印字するプログラムを書け。文字列やコメントの中の単語は数えるな。6はコマンドラインからセットできるパラメータにせよ。

いろいろと注文が多いですね。コメントを無視するという処理は予め予習しておいたので大丈夫だと思います。

やってみます。

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

#define WORDMAX 100

struct NODE {
    char *word;
    int count;
    struct NODE *left;
    struct NODE *right;
};
typedef struct NODE NODE;

/*
bar bar bar bar bar bar bar bar 
*/

// foo foo foo foo foo foo foo

NODE *create_node (char *str);
NODE *add_node (NODE *head,char *str);
void inorder(NODE *n);
int getword (char *word,int max);
int checkline (int c);

NODE *create_node (char *str) {
    NODE *node;
    size_t len = strlen(str);
    node = (NODE *)malloc(sizeof(NODE));
    if ( node == NULL ) {
        return NULL;
    }
    node->count = 1;
    node->right = node->left = NULL;
    node->word = (char *)malloc(len+1);
    if ( node->word == NULL ) {
        return NULL;
    }
    strcpy_s(node->word,len+1,str);
    return node;
}

NODE *add_node (NODE *p,char *str) {
    int strtype;
    if ( p == NULL ) {
        p = create_node(str);
    }
    else if ( (strtype = strcmp(str,p->word)) == 0 ) {
        p->count++;
    }
    else if ( strtype > 0 ) {
        p->right = add_node(p->right,str);
    }
    else {
        p->left = add_node(p->left,str);
    }
    return p;
}

// 通りがけ順のなぞり
void inorder(NODE *n){
    if( n == NULL ) return;
    inorder( n->left );
    printf("%s = %d\n", n->word,n->count );
    inorder( n->right );
}

int checkline (int c) {
    static int linebegin    = 0; // 始めの/フラグ用
    static int linecomment  = 0; // 一行コメント用のフラグ
    static int linescomment = 0; // 複数行コメント用のフラグ
    static int linesend     = 0; // 複数行コメント用の終了タグフラグ
    static int linesq       = 0; // 文字用シングルクォート
    static int linedq       = 0; // 文字列用ダブルクォート
    
    // シングルクォートの場合、次のシングルクォートまで削除
    if ( linesq ) {
        if ( c == '\'' ) linesq = 0;
        return 1;
    }
    
    // ダブルクォートの場合、次のダブルクォートまで削除
    if ( linedq ) {
        if ( c == '"' ) linedq = 0;
        return 1;
    }
    
    // 一行コメントの場合、改行コードまでを削除
    if ( linecomment ) {
        if ( c == '\n') linecomment = 0;
        return 1;
    }
    
    // 複数行コメントの場合、*/が見つかるまで削除
    if ( linescomment ) {
        if ( linesend ) {
            if ( c == '/' ) linescomment = 0;
            linesend = 0;
        }
        else {
            if ( c == '*' ) linesend = 1;
        }
        return 1;
    }
    
    // 一行、複数行なのかをチェックする
    if ( linebegin ) {
        if ( c == '/' ) {
            linecomment = 1;
        }
        else if ( c == '*' ) {
            linescomment = 1;
        }
        linebegin = 0;
        return 1;
    }
    
    // シングルクォートのチェック
    if ( !linesq && c == '\'' ) {
        linesq = 1;
        return 1;
    }
    
    // ダブルクォートのチェック
    if ( !linedq && c == '"' ) {
        linedq = 1;
        return 1;
    }
    
    // コメント開始/を発見したら一行コメントなのか複数コメントなのかを
    // チェックするためのフラグをONにする
    if ( !linebegin && c == '/' ) {
        linebegin = 1;
        return 1;
    }
    
    return 0;
}

// とりあえず英数字_のみ
int getword (char *word,int max) {
    int c;
    while( (c=getchar()) != EOF ) {
        
        // チェックに引っかかったら次へ
        if ( checkline(c) ) {
            continue;
        }
        
        if ( (isalnum(c)||c=='_') && --max ) {
            *word++ = (char)c;
        }
        else {
            break;
        }
    }
    *word = '\0';
    return c;
}

int main (int argc,char *argv[]) {
    NODE *head = NULL;
    char str[WORDMAX];
    size_t num = 6;
    size_t len;
    
    if ( argc > 1 ) {
        num = atoi(*++argv);
        if ( num <= 0 ) num = 6;
    }
    
    while ( getword(str,WORDMAX) != EOF ) {
        if ( (len = strlen(str)) > 0 ) {
            if ( len > num ) {
                str[num] = '\0';
            }
            head = add_node(head,str);
        }
    }
    
    inorder(head);
    return 0;
}
$ cat main.c | main 3
0 = 12
1 = 6
100 = 1
6 = 2
EOF = 2
NOD = 17
NUL = 8
WOR = 3
add = 5
arg = 4
ato = 1
bre = 1
c = 10
cha = 11
che = 3
con = 1
cou = 4
cre = 3
cty = 1
def = 1
els = 4
get = 4
h = 4
hea = 5
if = 14
inc = 4
ino = 5
int = 21
isa = 1
lef = 5
len = 6
lin = 8
mai = 1
mal = 2
max = 3
n = 7
nod = 10
num = 11
p = 10
pri = 1
ret = 9
rig = 5
siz = 4
sta = 6
std = 2
str = 27
typ = 1
voi = 2
whi = 2
wor = 10

できました。

一応仕様満たしてるかな?ちょっと問題文誤読してる可能性は無きにしも非ずですが。

コメントと文字列の除去がちょっと長くなってしまいました。最初はgetword関数に全部書いてたのですが、長くなったのでcheckline関数に分けてみました。

でもそうすると値の持続ができなかったので今回初めてstatic変数を定義してみました。うまく動いてるようでよかったです。