【问题标题】:Line counting and abberant results行数和异常结果
【发布时间】:2011-05-05 01:52:52
【问题描述】:

我正在编写一个实用程序来通过 Unix 命令行计算给定文件中的行数。通常这对我来说很简单,但显然我有一个重要的休息之夜。该程序的目标是从命令行获取未知数量的文件,将它们读入缓冲区并检查换行符。听起来很简单?

int size= 4096;

int main(int argc, char *argv[]){
  int fd, i, j, c, fileLines, totalLines;
  char *buf= (char *)malloc(size); //read buffer

  for (i=2; i<argc; i++){ //get first file

    fileLines=1;    

    if ((fd=open(argv[i], O_RDONLY))!= -1){ //open, read, print file count, close
        while ((c= read(fd, buf, size))!= 0){

            for (j=0; j<size; j++){
                if (buf[j] == '\n')
                    fileLines++;
            }
        }

    }
    printf("%s had %d lines of text\n", argv[i], fileLines);
    totalLines+= fileLines;
    close(fd);

  }

  printf("%d lines were counted overall\n", totalLines);    
  return 0;
}

我有两个问题。第一个是第一个 printf 语句永远不会在调试器之外执行。第二件事是totalLines打印输出大约应该是175K行,但是打印出来的值大约是767倍。

我无法理解这一点,因为所有相关变量都已声明超出其修改的范围,但这仍然不能解释为什么在调试器之外忽略第一个打印语句和行计数器更新以及异常的 totalLines 结果

感谢任何帮助。

回答

建议进行两项更改。
第一个是将j&lt;size 更改为j&lt;c。虽然这不是所需的解决方案,但它遵循良好的编码约定

第二个是将i=2 更改为i=1。我拥有原始 start 变量的原因是我启动调试器可执行文件的方式。在gdb命令行中,我输入run lc1 f1.txt来启动调试器。这导致 arglist 包含三个变量,我不知道 run f1.txt 是否完全合适,因为我的教授通过第一个示例向我们介绍了 gdb。

【问题讨论】:

  • 一次只打开一个文件,因为 for 循环按顺序遍历 arg 列表

标签: c linux line-count


【解决方案1】:

你没有初始化totalLines。您在循环内递增它,但在第一次声明它时不要将其设置为 0。

还有,你为什么从i=2开始?这是第三个命令行参数,也是程序的第二个参数。这是您想要的,还是您想从程序的第一个参数开始?

正如其他人指出的那样,您应该使用j &lt; c 而不是j &lt; size

【讨论】:

  • 你说得对,程序在调试器中按预期执行。但是,命令行输出只显示了 totalLines 值为 0 的第二个 printf。此外,我查看了命令行参数。 arg[0] 是可执行文件的文件路径,arg[1] 是可执行文件名,arg[2] 是要处理的第一个输入文件
  • @Jason - 是的,这不是唯一的问题。正如其他人所指出的,请尝试在循环中使用 i = 1j &lt; c
  • @Jason - 这些论点真的很奇怪。我能找到的每个资源都说argv[0] 应该是程序名称,argv[1] 应该是程序的第一个参数。
  • @Jason:你需要再看看。 argv[1] 不可能是可执行文件名,除非你用myapp myapp somefile someotherfile 调用你的应用程序。
  • Ken White,请检查我的问题以找到解决问题的方法。混乱是我的教授在 gdb 命令行中启动可执行文件的方法的结果
【解决方案2】:

你的循环是错误的。应该是j=0; j&lt;c; j++。这可能不是您看到的错误的直接原因,但肯定会导致问题。

您是否尝试使用调试器单步执行代码?

【讨论】:

  • 你和 tmg 对这个错误的看法是对的,但它并没有改变结果。
  • 第一个打印输出显示在调试器中,但不在命令行中
【解决方案3】:

考虑:./program file.txt

argv[0] is "program"
argv[1] is "file.txt"

这意味着您的 for 循环从错误的索引开始,如果您只通过 cmd 行传递 1 个文件,您的代码将永远不会进入该循环!它应该从索引 1 开始:

for (i=1; i<argc; i++){

帮自己一个忙,在声明变量时初始化所有变量。是确保这些内存位置不会出现垃圾的唯一方法。

【讨论】:

  • 我刚刚在stackoverflow.com/questions/5891964/… 上就这个话题提出了一个问题,希望您能对此提出看法。
  • 正是这种调整起到了作用。原来我在 gdb 提示符下输入了run program file.txt1,这使我的 arglist 计数器减少了 1。
【解决方案4】:

首先,很好的问题。 :) 所有必要的代码,陈述清楚,很明显你已经完成了你的工作。 :)

在调试器中,您是如何启动程序的?我认为argv[2] 起点可能与未达到printf() 有关,但这取决于您的起点。更多详情如下。

几个cmets:

int size= 4096;

通常,C 预处理器宏用于这种幻数。我知道你的老师可能说过永远不要使用预处理器,但惯用的 C 会这样写:

#define SIZE 4096
for (i=2; i<argc; i++){ //get first file

试试i=1 -- argv[0] 是程序的名称,argv[1] 将是第一个命令行参数 -- 大概如果有人通过./wc foo 调用它,你想计算行数在文件foo 中。 :) (另外,您希望循环终止。:) 当然,如果您尝试编写 wc -l 的替代品,那么您的循环没问题,但如果有人搞砸了参数,则不是很有帮助。这可以安全地保留为以后的项目。 (如果您现在好奇,请阅读 getopt(3) 联机帮助页。:)

    if ((fd=open(argv[i], O_RDONLY))!= -1){
        while ((c= read(fd, buf, size))!= 0){

            for (j=0; j<size; j++){

您将在j&lt;size 处结束循环——但您仅在最后一个块中读取c 字符。您正在阅读最后一个街区的剩余垃圾。 (如果在 /proc/ 中生成的文件可能会返回短读取,以方便内核程序员,我不会感到惊讶。)

                if (buf[j] == '\n')
                    fileLines++;
            }
        }

    }
    printf("%s had %d lines of text\n", argv[i], fileLines);
    totalLines+= fileLines;

这是您第一次分配给totalLines。 :) 容易产生垃圾初始值。

    close(fd);

您可能应该将close(fd); 调用移到if((fd=open())) 块中;如果打开失败,这将调用close(-1);。没什么大不了的,但是如果您检查 close(2) 错误返回(始终是好的做法),它会返回一个不必要的错误。

  }

希望这会有所帮助!

【讨论】:

  • 将 i 更改为 1 就可以了。让我失望的是我让调试器显示 arglis 变量。 arg[0] 是可执行文件的文件路径,arg[1] 是可执行文件本身,arg[2] 是要处理的文本文件。谢谢!
【解决方案5】:

你可能知道wc,但我会提到它以防万一。

我知道它不能直接帮助您调试特定问题,但也许您可以查看源代码和/或使用它来验证您的程序是否正常工作。

【讨论】:

    【解决方案6】:

    你在 for() 循环中有逻辑错误。您应该使用“读取字节数”而不是“读取到”,我在您的代码中的意思是在 for() 中使用“c”而不是“size”

    【讨论】:

      猜你喜欢
      • 2020-12-29
      • 2016-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多