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

Mor*_*lon -2 c comments file count line

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

文件:
简单注释:N行
多行注释:N行
总行数:N行

我有这个:

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

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

    } while (c != EOF);
Run Code Online (Sandbox Code Playgroud)

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

Joh*_*ode 6

您可以编写一个应该处理大多数情况的状态机.

扫描文件时,您将处于以下状态之一:

  1. TEXT - 常规(未注释)文本; 这是你将开始的状态.在这种状态下看到的任何换行都会导致总行计数器递增.
  2. SAW_SLASH - 您已经看过单曲/,这可能是单行或多行评论的开始.如果下一个字符是a /,您将进入SINGLE_COMMENT状态.如果下一个字符是a *,您将进入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;
}
Run Code Online (Sandbox Code Playgroud)

编辑

这是一个与上面程序等效的表驱动.我创建一个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;
}
Run Code Online (Sandbox Code Playgroud)

自己运行文件给了我们

$ ./comment_counter comment_counter.c
File                 : comment_counter.c
Total lines          : 150
Single-comment lines : 7
Multi-comment lines  : 42
Run Code Online (Sandbox Code Playgroud)

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