K&R演習[1-24]

課題

括弧、中括弧、大括弧の釣り合いが取れていないといったプログラムの基本的な構文エラーのチェックを行なうプログラムを書け。引用符、二重引用符・コメントなどの処理も忘れないこと。

回答

/**
 * @file    プログラミング言語C 1-24
 * @brief   括弧、中括弧、大括弧の釣り合いが取れていないといったプログラムの基本的な構文エラーのチェックを行なうプログラムを書け。引用符、二重引用符・コメントなどの処理も忘れないこと。
 * @author  hiroyuki murai
 * @date    20171108
 * @note    
*/
#include <stdio.h>
#include <string.h>

typedef unsigned long int u8;

const char NG = 0;
const char OK = 1;
const char OFF = 0;
const char ON = 1;

/* 対応確認*/
enum {
    E_COMMENT,      /* コメント     */
    E_BRACKETS1,    /* 括弧         ()  */
    E_BRACKETS2,    /* 中括弧       {} */
    E_BRACKETS3,    /* 大括弧    */
    E_QUATATION,    /* 引用符       '  */
    E_DQUATATION,   /* 二重引用符   "  */
    E_NUM_CHK
};

// 関数プロトタイプ
void chk_err(char c, char s, char e, u8 cnt_line);

u8 startline[100];

u8 start_cnt[E_NUM_CHK];
u8 end_cnt[  E_NUM_CHK];

/**
 * @fn      Main
 * @brief
 * @param
 * @return
 * @detail
 */
int main(void)
{
    int c;
    char str[10000];
    u8 pos_str = 0, cnt_line = 0;
    char flg[E_NUM_CHK];
    char flg_err[E_NUM_CHK];
    char flg_jdg;
    FILE *fp;
    char *p;

    for (c = 0; c < E_NUM_CHK; c++) {       // 開始終了の確認数
        flg[c] = OFF;                   // 開始終了フラグ
        flg_err[c] = OFF;               /* エラーフラグ */
    }

    fp = fopen("1-24.c", "r");              // ファイルを開く
    if (fp == NULL) {                       // ファイルが開けなかったら
        printf("failed to open file\n");    /* エラー表示 */
        return 0;                           // 終了
    }

    while ((c = fgetc(fp)) != EOF)  {       /* ファイル終端まで */
        str[pos_str] = c;                   /* 入力文字を格納 */
        if (c == '\n') ++cnt_line;          // 行数を数える

        // コメント開始判定
        if ((str[pos_str - 1] == '/') &&    /* 今回が'/'で*/
            (str[pos_str    ] == '*')) {    /* 前回が'*'なら */
            flg[E_COMMENT] = ON;
        }

        // コメント終了判定
        if ((str[pos_str - 1] == '*') &&    /* 今回が'*'で  */
            (str[pos_str    ] == '/')) {    /* 前回が'/'で  */
            if (flg[E_COMMENT] == ON) {     // コメント開始中なら
                flg[E_COMMENT] = OFF;       // コメント終了
            } else {
                flg_err[E_COMMENT] = ON;    /* コメント開始していないならエラー */
            }
        }

        // コメント中でなければ確認
        if (flg[E_COMMENT] == OFF) {
            chk_err(c, '(', ')', cnt_line);     // 括弧
            chk_err(c, '{', '}' , cnt_line);    // 中括弧
            chk_err(c, '[', ']', cnt_line);     // 大括弧
            chk_err(c, 0x27, 0x27, cnt_line);   // 引用符
            chk_err(c, 0x22, 0x22, cnt_line);   // 二重引用符
        }

        pos_str++;  // 次の文字へ
        printf("%c", c);    // 文字表示
    }

    /* 開始と終了が不一致ならエラーとする  */
    for (c = 0; c < E_NUM_CHK; c++) {
        if (start_cnt[c] != end_cnt[c]) {
            switch (c) {
                case E_BRACKETS1:   
                case E_BRACKETS2:   
                case E_BRACKETS3:
                    if (start_cnt[c] != end_cnt[c]) {
                        flg_err[c] = ON;    /* エラーフラグ */
                    }
                    break;
                    
                case E_QUATATION:   
                case E_DQUATATION:  
                    if ((start_cnt[c] % 2) != 0) {
                        flg_err[c] = ON;    /*  エラーフラグ */
                    }
                    break;

            }
        }
    }

    // 総合判定
    printf("--- Judgement--- \n");
    flg_jdg = OK;       // 初期値:OK
    for (c = 0; c < E_NUM_CHK; c++) {       // 開始終了の確認数
        if (flg_err[c] == ON) {     /*  一つでもNGなら */
            flg_jdg = NG;           // NG
            switch (c) {
                case E_COMMENT  :   printf("comment error\n");          break;  // コメント
                case E_BRACKETS1:   printf("brackets1 error\n");        break;  // 括弧         ()
                case E_BRACKETS2:   printf("brackets2 error\n");        break;  // 中括弧       {}
                case E_BRACKETS3:   printf("brackets3 error\n");        break;  // 大括弧   []
                case E_QUATATION:   printf("quatation error\n");        break;  /* 引用符       ' */
                case E_DQUATATION:  printf("double quatation error\n"); break;  /* 二重引用符   " */
            }
        }
    }
    if (flg_jdg == OK) {        // 全てOKなら
        printf("All OK\n");     // OK表示
    }

    /* 集計結果 */
    printf("brackets1:%3lu, %3lu\n", start_cnt[E_BRACKETS1],  end_cnt[E_BRACKETS1]);
    printf("brackets2:%3lu, %3lu\n", start_cnt[E_BRACKETS2],  end_cnt[E_BRACKETS2]);
    printf("brackets3:%3lu, %3lu\n", start_cnt[E_BRACKETS3],  end_cnt[E_BRACKETS3]);
    printf("quatation:%3lu, %3lu\n", start_cnt[E_QUATATION],  end_cnt[E_QUATATION]);
    printf("dq       :%3lu, %3lu\n", start_cnt[E_DQUATATION], end_cnt[E_DQUATATION]);

}


/**
 * @fn      chk_err
 * @brief   
 * @param   (char) c = 判定対象文字
            (char) s = 開始文字
            (char) e = 終了文字
            (char) *flg = 開始フラグ
            (char) *flg_err = エラーフラグ
 * @return
 * @detail
 */
void chk_err(char c, char s, char e, u8 cnt_line)
{

    if (c == s) {           // 開始文字なら
        switch (c) {
            case '(':   start_cnt[E_BRACKETS1]++;   break;  // 括弧
            case '{':   start_cnt[E_BRACKETS2]++;   break;  // 括弧
            case '[':   start_cnt[E_BRACKETS3]++;   break;  // 大括弧
            case 0x27:  start_cnt[E_QUATATION]++;   break;  // 引用符
            case 0x22:  start_cnt[E_DQUATATION]++;  break;  // 二重引用符
        }

    }
    if (c == e) {           // 終了文字なら
        switch (c) {
            case ')':   end_cnt[E_BRACKETS1]++;     break;  // 括弧
            case '}':   end_cnt[E_BRACKETS2]++;     break;  // 括弧
            case ']':   end_cnt[E_BRACKETS3]++;     break;  // 大括弧
            case 0x27:  end_cnt[E_QUATATION]++;     break;  // 引用符
            case 0x22:  end_cnt[E_DQUATATION]++;    break;  // 二重引用符
        }       
    }
}

/* eof */

実行内容

上記のCソースをそのまま読み込ませた

実行結果

--- Judgement---
All OK
brackets1: 55,  55
brackets2: 27,  27
brackets3: 49,  49
quatation: 34,  34
dq       : 34,  34

補足

ダブルスラッシュをコメントとして登録していないため、コメント中の日本語を検索する。
一部の日本語コメントを/* */に変更すると解決した。