C言語ケーススタディ ファイルの行数・単語数をカウントするプログラム3(ワイルドカードへの対応)




2014年10月より個人の方を対象に、Study C無料提供を開始しました。
C言語を勉強中の方は、学習・教育に最適なC言語インタープリタのStudy Cを使ってみてください(個人の方は無料です)。
大学・高専・高校などの教育機関での採用実績も多数あるロングセラー商品Study Cが、個人向けに無料提供を始めました。
インタープリタの手軽さに加え、ゲームや3Dタートルグラフィックで楽しく勉強したりと、C言語の学習を強力にサポートします。
ブロック崩しゲーム 3Dツリー クリスマスツリー
また、このようなボタンの用意されているページでは、掲載しているプログラムをStudy Cに直接ロードし実行したりすることができます。
Study Cにロードする Study Cにロードし編集する Study Cにロードし実行する
Study C無料利用についての詳細は、このページを参照してください。



コマンドラインにワイルドカードを指定できるようにするには、プログラムでファイルに展開する必要があります。

プログラムでワイルドカードをファイルに展開する主な手順は以下のようになります。
(1)argvで渡された文字列に(strpbrk()関数を使用して'?'、'*'文字を検査)ワイルドカードが含まれているかチェックします。
(2)ワイルドカードが使われていない場合はそのままargv[]の文字列を処理します。
(3)ワイルドカードが使われている場合はFindFirstFile/FindNextFile/FindClose関数を使用してファイルを展開します。
(4)内部のループでファイルの展開を行います。

下記のプログラムはコマンドラインでワイルドカードを使用する場合の基本的な形式になると思います(汎用に使用できると思います)。 Study C以外のコンパイラで使用する場合はwindows.hファイルをインクルードして使用してください。

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


void    main(int argc, char *argv[])
{
        char    *ptr, dir[260], file[260];
        int     i1;
        WIN32_FIND_DATA fd;
        HANDLE  h;

        if(argc < 2){
                printf("コマンドの入力形式が間違っています.\n");
                return;
        }

        for(i1 = 1; i1 < argc; i1++){
                if(strpbrk(argv[i1], "*?") == NULL){

                        /*処理する関数をここで呼び出します(ファイル名はargv[i1]に格納)*/

                        continue;
                }
                strcpy(dir, argv[i1]);
                ptr = strrchr(dir, '\\');
                if(ptr == NULL){
                        dir[0] = '\0';
                }
                else{
                        *(ptr+1) = '\0';
                }
                if ((h = FindFirstFile(argv[i1], &fd)) == INVALID_HANDLE_VALUE){
                        printf("コマンドの入力形式が間違っています.\n");
                        return;
                }
                do {
                        strcpy(file, dir);
                        strcat(file, fd.cFileName);

                        /*処理する関数をここで呼び出します(ファイル名はfileに格納)*/

                }while (FindNextFile(h, &fd));
                FindClose(h);
        }
}

注)このプログラムもファイル名にたいしてstrrchr()、strpbrk()関数を使用しているので部分があるので、全角文字のファイル名やディレクトリ名では正常に動作しません。
strrchar()関数を_mbsrchr()関数、 strpbrk()関数を_mbspbrk()関数に変更すれば正しく動作します(単純に関数名を置き換えれば正しく動作するようになります - Study C以外では適切なインクルードファイルの指定も必要です)。 詳しくは、_mbsrchr()、_mspbrk()関数を参照してください。

上記の基本部分を前回のプログラムに反映させたのが次のプログラムになります。

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


unsigned long
        total_c, total_w, total_l;

int     WordCount(char *fname);

void    main(int argc, char *argv[])
{
        char    *ptr, dir[260], file[260];
        int     i1;
        WIN32_FIND_DATA fd;
        HANDLE  h;

        if(argc < 2){
                printf("コマンドの入力形式が間違っています.\n");
                return;
        }

        total_l = total_w = total_c = 0;
        for(i1 = 1; i1 < argc; i1++){
                if(strpbrk(argv[i1], "*?") == NULL){
                        if(WordCount(argv[i1]) == 0){
                                return;
                        }
                        continue;
                }
                strcpy(dir, argv[i1]);
                ptr = strrchr(dir, '\\');
                if(ptr == NULL){
                        dir[0] = '\0';
                }
                else{
                        *(ptr+1) = '\0';
                }
                if ((h = FindFirstFile(argv[i1], &fd)) == INVALID_HANDLE_VALUE){
                        printf("コマンドの入力形式が間違っています.\n");
                        return;
                }
                do {
                        strcpy(file, dir);
                        strcat(file, fd.cFileName);
                        if(WordCount(file) == 0){
                                return;
                        }
                }while (FindNextFile(h, &fd));
                FindClose(h);
        }

        printf("%7ld %7ld %7ld TOTAL\n", total_l, total_w, total_c);
}


WordCount(char *fname)
{
        FILE    *fp;
        char    buff[1024], *ptr;
        int     len;
        unsigned char
                *file,
                *line_buff,
                *wp;
        int     i1, num, blank;
        long    character,word,line;

        fp = fopen(fname, "r");
        if(fp == NULL){
                printf("ファイルがオープンできません[%s].\n", fname);
                return(0);
        }

        blank = 0;
        character = word = line = 0;
        while((num = fread(buff, 1, 1024, fp)) > 0){
                for(wp = buff; num > 0; num--,wp++){
                        if(*wp <= ' '){
                                if(*wp == '\n'){
                                        line++;
                                        character++;
                                }
                                if( !blank )
                                        word++;
                                blank = 1;
                                character++;
                        }
                        else{
                                blank = 0;
                                character++;
                        }
                }
        }
        total_l += line;
        total_w += word;
        total_c += character;

        ptr = strrchr(fname, '\\');
        if(ptr == NULL)
                ptr = fname;

        printf("%7ld %7ld %7ld %s\n", line, word, character, ptr);
        fclose(fp);
        return(1);
}

上記のプログラムは"run *.c *.h"と言ったような使い方ができるようになります。