C程序来计算注释行(//和/ * * /)

时间:2013-05-30 20:12:30

标签: c file count comments line

我需要一个程序来计算.txt或.c文件的行数并返回以下内容:

档案:
简单评论:N行
多行注释:N行
总行数:N行

我有这个:

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

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

    } while (c != EOF);

我不知道如何实现其余部分。
我也尝试使用strstr()函数,但也没有得到它。

2 个答案:

答案 0 :(得分: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状态。
  6. 有些边缘情况我没有处理(例如在评论状态下遇到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;
    }
    

    修改

    这是一个与上面程序等效的表驱动。我创建一个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
    

    我认为是对的。这与第一个版本具有相同的弱点,只是形式不同。

答案 1 :(得分:0)

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

你需要另一个变量,每当你看到一个字符/后面跟着另一个变量/时(这很容易,就像你基本上做n ++一样,但它应该看起来像

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

然后对于多行注释,你做同样的事情,并且只计算/ ,就像上面一样。你只需要关注n何时增加(每一行),以及当其他增加时(对于//每行一次,对于以/ 开头的每一行一次,直到你击中一个 */)。