2013-05-30 27 views
-2

我需要一個程序來計算.txt或.c文件的行並返回我下面的事情:C程序來算註釋行(//和/ * * /)

文件:
簡單註釋:N線
多行註釋:N線
行總數:N線

我有這樣的:

if (pFile != NULL) 
{ 
    do { 
    c = fgetc(pFile); 

    if (c == '\n') n++; 

    } while (c != EOF); 

我不知道如何實現它的其餘部分。
我試着用strstr()函數,也沒有得到它。

+1

這是一個perl的工作,而不是c。 –

+1

這樣做的最好方法是編寫一個解析器,它不能完全正確,只是字符檢查。 – squiguy

+1

您需要編寫一個基於c標準的解析器(僅僅是前幾個翻譯階段),否則您將花費​​無盡的時間來追蹤像「這些是註釋分隔符/ * // * /」這樣的情況,而且三字母和行連續不是你的朋友 – tletnes

回答

6

您可以編寫一個應該處理大多數情況的狀態機。

當你掃描的文件,你會在下面的狀態之一:

  1. TEXT - 常規(非註釋)文本;這是你開始的狀態。在這種狀態下看到的任何換行都會導致總數計數器增加。
  2. SAW_SLASH - 您已經看過一個/,這可能是單行或多行註釋的開始。如果下一個字符是/,則會進入SINGLE_COMMENT狀態。如果下一個字符是*,那麼您將進入MULTI_COMMENT狀態。對於任何其他角色,您可以回到TEXT狀態。
  3. SINGLE_COMMENT - 您已經看到了//令牌;你會保持這種狀態,直到你看到一個換行符;一旦看到換行符,就會增加單行註釋的數量以及總行數,並返回到TEXT狀態。
  4. MULTI_COMMENT - 您已經看到了/*令牌;你會保持這種狀態,直到你看到下一個*/令牌。您在此狀態下看到的任何換行符都會導致多註釋行計數器隨總行數一起遞增。
  5. SAW_STAR - 處於MULTI_COMMENT狀態時,您看到一個單獨的*。如果下一個字符是/,您將返回TEXT狀態。如果下一個字符是*,您將保持SAW_STAR狀態。否則,您將返回到MULTI_COMMENT狀態。

有些邊緣情況我沒有處理(例如在評論狀態下遇到EOF),但下面應該是一個合理的例子,說明如何做這樣的事情。

請注意,嵌套註釋將不會被計數;即如果一個//-有限註釋出現在/* */-有限註釋內,則只有多註釋計數器將被更新。

您可能想要將計數邏輯分解爲自己的函數;只是儘量保持這個例子儘可能簡單。

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

/** 
* Count up the number of total lines, single-comment lines, 
* and multi-comment lines in a file. 
*/ 
int main(int argc, char **argv) 
{ 
    FILE *fp; 
    int c; 
    unsigned int chars = 0; 
    unsigned int total = 0; 
    unsigned int multi = 0; 
    unsigned int single = 0; 

    enum states { TEXT, 
       SAW_SLASH, 
       SAW_STAR, 
       SINGLE_COMMENT, 
       MULTI_COMMENT } state = TEXT; 

    if (argc < 2) 
    { 
    fprintf(stderr, "USAGE: %s <filename>\n", argv[0]); 
    exit(0); 
    } 

    fp = fopen(argv[1], "r"); 
    if (!fp) 
    { 
    fprintf(stderr, "Cannot open file %s\n", argv[1]); 
    exit(0); 
    } 

    while ((c = fgetc(fp)) != EOF) 
    { 
    chars++; 
    switch(state) 
    { 
     case TEXT : 
     switch(c) 
     { 
      case '/' : state = SAW_SLASH; break; 
      case '\n' : total++; // fall-through 
      default : break; 
     } 
     break; 

     case SAW_SLASH : 
     switch(c) 
     { 
      case '/' : state = SINGLE_COMMENT; break; 
      case '*' : state = MULTI_COMMENT; break; 
      case '\n' : total++; // fall through 
      default : state = TEXT; break; 
     } 
     break; 

     case SAW_STAR : 
     switch(c) 
     { 
      case '/' : state = TEXT; multi++; break; 
      case '*' : break; 
      case '\n' : total++; multi++; // fall through 
      default : state = MULTI_COMMENT; break; 
     } 
     break; 

     case SINGLE_COMMENT : 
     switch(c) 
     { 
      case '\n' : state = TEXT; total++; single++; // fall through 
      default : break; 
     } 
     break; 

     case MULTI_COMMENT : 
     switch(c) 
     { 
      case '*' : state = SAW_STAR; break; 
      case '\n' : total++; multi++; // fall through 
      default : break; 
     } 
     break; 

     default: // NOT REACHABLE 
     break; 
    } 
    } 

    fclose(fp); 

    printf("File     : %s\n", argv[1]); 
    printf("Total lines   : %8u\n", total); 
    printf("Single-comment lines : %8u\n", single); 
    printf("Multi-comment lines : %8u\n", multi); 
    return 0; 
} 

EDIT

這裏的一個表格驅動等效於上面的程序。我創建了一個state表來控制狀態轉換,並創建一個action表來控制當我改變狀態時發生的情況。

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

/** 
* Using preprocessor macros instead of enums, per request; normally 
* I would use enums, since they obey scoping rules and 
* show up in debuggers. 
*/ 
#define TEXT   0 
#define SAW_SLASH  1 
#define SAW_STAR  2 
#define SINGLE_COMMENT 3 
#define MULTI_COMMENT 4 

#define TOTAL_STATES 5 

#define NO_ACTION  0 
#define INC_TOTAL  1 
#define INC_SINGLE  2 
#define INC_MULTI  4 

/** 
* This example assumes 7-bit ASCII, for a total of 
* 128 character encodings. You'll want to change this 
* to handle other encodings. 
*/ 
#define ENCODINGS 128 

/** 
* Need a state table to control state transitions and an action 
* table to specify what happens on a transition. Each table 
* is indexed by the state and the next input character. 
*/ 
static int state[TOTAL_STATES][ENCODINGS]; // Since these tables are declared at file scope, they will be initialized to 
static int action[TOTAL_STATES][ENCODINGS]; // all elements 0, which correspond to the "default" states defined above. 

/** 
* Initialize our state table. 
*/ 
void initState(int (*state)[ENCODINGS]) 
{ 
    /** 
    * If we're in the TEXT state and see a '/' character, move to the SAW_SLASH 
    * state, otherwise stay in the TEXT state 
    */ 
    state[TEXT]['/'] = SAW_SLASH; 

    /** 
    * If we're in the SAW_SLASH state, we can go one of three ways depending 
    * on the next character. 
    */ 
    state[SAW_SLASH]['/'] = SINGLE_COMMENT; 
    state[SAW_SLASH]['*'] = MULTI_COMMENT; 
    state[SAW_SLASH]['\n'] = TEXT; 

    /** 
    * For all but a few specific characters, if we're in any one of 
    * the SAW_STAR, SINGLE_COMMENT, or MULTI_COMMENT states, 
    * we stay in that state. 
    */ 
    for (size_t i = 0; i < ENCODINGS; i++) 
    { 
    state[SAW_STAR][i] = MULTI_COMMENT; 
    state[SINGLE_COMMENT][i] = SINGLE_COMMENT; 
    state[MULTI_COMMENT][i] = MULTI_COMMENT; 
    } 

    /** 
    * Exceptions to the loop above. 
    */ 
    state[SAW_STAR]['/'] = TEXT; 
    state[SAW_STAR]['*'] = SAW_STAR; 

    state[SINGLE_COMMENT]['\n'] = TEXT; 
    state[MULTI_COMMENT]['*'] = SAW_STAR; 
} 

/** 
* Initialize our action table 
*/ 
void initAction(int (*action)[ENCODINGS]) 
{ 
    action[TEXT]['\n'] = INC_TOTAL; 
    action[SAW_STAR]['/'] = INC_MULTI; 
    action[MULTI_COMMENT]['\n'] = INC_MULTI | INC_TOTAL; // Multiple actions are bitwise-OR'd 
    action[SINGLE_COMMENT]['\n'] = INC_SINGLE | INC_TOTAL; // together 
    action[SAW_SLASH]['\n'] = INC_TOTAL; 
} 

/** 
* Scan the input file for comments 
*/ 
void countComments(FILE *stream, size_t *totalLines, size_t *single, size_t *multi) 
{ 
    *totalLines = *single = *multi = 0; 

    int c; 
    int curState = TEXT, curAction = NO_ACTION; 

    while ((c = fgetc(stream)) != EOF) 
    { 
    curAction = action[curState][c]; // Read the action before we overwrite the state 
    curState = state[curState][c]; // Get the new state (which may be the same as the old state) 

    if (curAction & INC_TOTAL)  // Execute the action. 
     (*totalLines)++; 

    if (curAction & INC_SINGLE) 
     (*single)++; 

    if (curAction & INC_MULTI) 
     (*multi)++; 
    } 
} 

/** 
* Main function. 
*/ 
int main(int argc, char **argv) 
{ 
    /** 
    * Input sanity check 
    */ 
    if (argc < 2) 
    { 
    fprintf(stderr, "USAGE: %s <filename>\n", argv[0]); 
    exit(EXIT_FAILURE); 
    } 

    /** 
    * Open the input file 
    */ 
    FILE *fp = fopen(argv[1], "r"); 
    if (!fp) 
    { 
    fprintf(stderr, "Cannot open file %s\n", argv[1]); 
    exit(EXIT_FAILURE); 
    } 

    /** 
    * If input file was successfully opened, initialize our 
    * state and action tables. 
    */ 
    initState(state); 
    initAction(action); 

    size_t totalLines, single, multi; 

    /** 
    * Do the thing. 
    */ 
    countComments(fp, &totalLines, &single, &multi); 
    fclose(fp); 

    printf("File     : %s\n", argv[1]); 
    printf("Total lines   : %zu\n", totalLines); 
    printf("Single-comment lines : %zu\n", single); 
    printf("Multi-comment lines : %zu\n", multi); 

    return EXIT_SUCCESS; 
} 

在自身運行的文件給我們

$ ./comment_counter comment_counter.c 
File     : comment_counter.c 
Total lines   : 150 
Single-comment lines : 7 
Multi-comment lines : 42 

我認爲這是正確的。這與第一個版本有着同樣的弱點,只是形式不同而已。

+0

嘿約翰。你能幫我嗎?我有一個任務是編寫一個C程序來計算另一個文件中的註釋數量。這是你寫在這裏的一個很好的例子。我對你的例子做了一些改變,現在它解決了我的任務。但有沒有辦法使用枚舉?你的解決方案的一些選擇? – Alex

+1

@Alex:您可以使用預處理器宏而不是枚舉;我只是選擇枚舉,因爲它們遵守範圍規則,並且名稱保存在調試器中。你可以隱藏嵌套的'switch'語句並使用表驅動的方法。 –

+0

問題是我們一個月前在大學開始學習C,而我對這些事情並不是很熟悉。我只是迷上了所有嵌套的開關。 – Alex

0

所以n ==文件中的行數。

你需要計數每次你看到的字符/緊接着又/(這是很容易,同樣,你正在做的n ++主要方式時另一個變量,但它應該看起來像

if(c == '/') c = fgetc(pFile); if(c == '/') comments++; else break; 

然後對於多行註釋,你可以做同樣的事情,只需要計算/ ,就像上面一樣。只需要注意n何時遞增(每行),以及何時遞增(一次對於每一行//和每行以/開頭並且直到您點擊* /)。

+1

你可能想考慮這個//註釋//而不是這個註釋 所以,當你在if()中做註釋++時,不斷地提取字符,直到獲得換行符或eof。其餘的東西是我想確定單行註釋 – Abhinav