【问题标题】:How to count newlines within a file, but not counting lines that are just newlines?如何计算文件中的换行符,但不计算只是换行符的行?
【发布时间】:2017-04-25 23:35:38
【问题描述】:

为了正确解析输入,我需要能够计算文件中的行数。但是,我不想计算只是换行符的行。为了解决这个问题,我创建了以下函数:

int countLinesInFile(char *filename) {
  int newlines = 0;

  if (access(filename,F_OK) != -1)
    error("File not found",0);

  FILE *input = fopen(filename,"r");

  int size = 256 * 4;
  char buffer[size];
  while ((fgets(buffer,sizeof(buffer),input)) != EOF) {
    printf("Read a string");
    if (buffer == "\n")
      continue;
    newlines++;
  }

  fclose(input);
  return newlines;
}

在文件的顶部,我有以下内容:

#include <stdio.h>
#include <unistd.h>

当我运行程序并尝试计算行数时,它会出现分段错误。使用 valgrind,我可以看到以下内容:

==6632== Invalid read of size 4
==6632==    at 0x4EA8E6B: fgets (in /usr/lib64/libc-2.24.so)
==6632==    by 0x402219: countLinesInFile (in [executable])
[other information about program, does not seem relevant]
==6632==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==6632== 
==6632== 
==6632== Process terminating with default action of signal 11 (SIGSEGV)
==6632==  Access not within mapped region at address 0x0
==6632==    at 0x4EA8E6B: fgets (in /usr/lib64/libc-2.24.so)
==6632==    by 0x402219: countLinesInFile (in [executable])
[other information about program, does not seem relevant]
==6632==  If you believe this happened as a result of a stack
==6632==  overflow in your program's main thread (unlikely but
==6632==  possible), you can try to increase the size of the
==6632==  main thread stack using the --main-stacksize= flag.
==6632==  The main thread stack size used in this run was 8388608.
==6632== 
==6632== HEAP SUMMARY:
==6632==     in use at exit: 475 bytes in 16 blocks
==6632==   total heap usage: 19 allocs, 3 frees, 3,075 bytes allocated
==6632== 
==6632== LEAK SUMMARY:
==6632==    definitely lost: 0 bytes in 0 blocks
==6632==    indirectly lost: 0 bytes in 0 blocks
==6632==      possibly lost: 0 bytes in 0 blocks
==6632==    still reachable: 475 bytes in 16 blocks
==6632==         suppressed: 0 bytes in 0 blocks
==6632== Rerun with --leak-check=full to see details of leaked memory
==6632== 
==6632== For counts of detected and suppressed errors, rerun with: -v
==6632== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

我试图在 while 循环的开头添加一行“printf(“Reading file”)”。这段代码不执行,我相信 fgets 是问题所在。不幸的是,我不知道这个问题是什么。

我已验证我要读取的文件确实包含正确的文本,并且不为空。

我创建的函数是这个任务的正确方法吗?如果是这样,我会遇到什么问题?以后如何避免这个问题?

更新:这对我来说确实是一个愚蠢的错误。我正在使用 Valgrind 运行程序,看起来它不使用可执行文件的目录,这意味着它找不到文件。感谢您的帮助。

【问题讨论】:

  • 首先检查fopen的返回值。
  • buffer == "\n" 这不是C中比较字符串的方式。需要使用strcmp或字符比较buffer[0] == '\n'
  • fgets 不返回 EOF
  • @BLUEPIXY 我检查了 fopen 的输出,它说“找不到文件”,尽管有一个“stuff.br”(正在打开的文件的名称,我检查了)同一个目录。
  • 该文件是否与可执行文件存在于同一目录中?在 IDE 中运行时,往往不是同一个目录。尝试指定文件名的完整路径。另外,如果您输入用户的文件名,您是否正在删除换行符?

标签: c file newline c99 fgets


【解决方案1】:

有两件事:首先,fgets 在无法读取更多行时返回NULL,而不是EOF。因此,条件应为while(fgets(...) != NULL) 或短while(fgets(...))。 其次,buffer == "\n" 比较两个指向字符的指针,即比较两个内存地址。任何东西都不太可能与字符串文字"\n"具有相同的内存地址。因此,比较字符,即buffer[0]=='\n'buffer[0]!='\n'。 而且我认为您可以轻松摆脱continue-statement,这样代码如下所示:

  while (fgets(buffer,sizeof(buffer),input)) {
    if (buffer[0] != '\n') {
      newlines++;
    }
  }

【讨论】:

  • 我这样做了,但它继续出现段错误。我认为问题在于第一次调用 fgets。
【解决方案2】:

我认为您需要跟踪您最后一个 \n 的文件偏移量,如果此 \n 的位置 == last+1 则不要增加。

【讨论】:

    【解决方案3】:

    函数fgets 返回一个指针值,失败时返回NULL。 EOF(通常,除非您已更改)定义为-1。它们永远不会比较相等,这意味着您将在停止之后很久才调用 fgets。

    【讨论】:

    • 不幸的是,这意味着如果我添加了一条在运行循环时打印的行,它就会输出,我确实尝试过。不幸的是,它没有。我认为这意味着第一个 fgets 失败了。
    【解决方案4】:

    fgets() 将您限制为预定义的行长。要解决这个问题,您可以使用POSIX-standard getline()

    在计算行数时,要跳过空行,您可以简单地对匹配 "\n" 的任何内容进行打折:

    #include <stdio.h>
    
    ...
    
    long countLines( const char *filename )
    {
        FILE *fp = fopen( filename, "r" );
        if ( fp == NULL )
        {
            return( -1L );
        }
    
        char *line = NULL;
        size_t bytes = 0UL;
        long lineCount = 0L;
    
        for ( ;; )
        {
            ssize_t result = getline( &line, &bytes, fp );
            if ( -1 == result )
            {
                break;
            }
    
            if ( strcmp( line, "\n" ) )
            {
                lineCount++;
            }
        }
    
        free( line );
        fclose( fp );
    
        return( lineCount );
    }
    

    【讨论】:

      【解决方案5】:
      FILE *input = fopen(filename,"r");
      

      应该是

      FILE *input;
      input = fopen(filename,"r");
      

      我不相信fgets() 返回换行符或EOF。它在换行符之前的字符处停止读取,下一次读取将是换行符之后的字符或连续的换行符。

      您可能需要使用一个二进制文件读取函数来一次读取整个文件,然后解析出这些行。这也将处理最后没有 EOF 的文本文件的情况。许多人没有

      • 获取文件的大小。
      • 分配一个缓冲区来保存整个文件。
      • 将整个文件读入缓冲区。
      • 解析缓冲区。

      C library function - fgets()
      C File I/O and Binary File I/O

      【讨论】:

      • FILE *input = fopen(filename,"r"); 有什么问题? “使用二进制文件读取功能”为什么文本文件需要二进制文件读取器?
      • 两行代码没有任何优点。你对fgets() 的看法是错误的——具体来说,它在数据中包含换行符,除非没有空间。它通过返回一个空指针来指示 EOF。绝对不需要将整个文件读入缓冲区。你可以用三个整数(最后一个字符、这个字符、总计数)和getc()来完成这项工作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 2011-03-14
      相关资源
      最近更新 更多