-2

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

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

我有这个:

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

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

    } while (c != EOF);

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

4

2 回答 2

7

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

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

  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;
}

编辑

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

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

于 2013-05-30T21:47:51.327 回答
0

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

您将需要另一个变量,每次看到字符 / 后跟另一个 / 时都会计数(这很简单,基本上与您执行 n++ 的方式相同,但它应该看起来像

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

然后对于多行注释,你做同样的事情,只是计数 / ,就像上面一样。您只需要注意 n 何时增加(每行),以及其他的何时增加(每行一次以//开头,每行一次以/开头,直到您点击 */ )。

于 2013-05-30T20:19:21.743 回答