【问题标题】:Reading files using system calls and printing lines使用系统调用和打印行读取文件
【发布时间】:2016-01-20 15:19:13
【问题描述】:

该程序读取一个文本文件“hello.txt”并查找其中出现的字符串 w 并打印行号和整行。它还打印字符串 w 在文件中出现的次数。程序编译没有错误,代码如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

int main() {

    int fd;
    char c;
    char str[152];
    int i = 0, j = 0;
    int bytesread;
    int flag = 1;
    int found = 0;
    int line = 1;
    int foundflag = 1;

    char w[] = {'h', 'e', 'l', 'l', 'o'};
    int len = strlen(w);

    if ((fd = open("hello.txt", O_RDONLY, 0)) != -1) { //if 1

        bytesread = read(fd, &c, 1);
        str[j] = c;
        j++;

        if (bytesread != -1) { //if 2

            while (bytesread != 0) { //while

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

                if (c == w[i]) { //if 3
                    i++;
                    flag = 0;
                } else if (flag == 0 || i == len) //end of f3
                { // else 3
                    i = 0;
                    flag = 1;
                }// end of else 3
                else if (flag == 1) {
                    while (read(fd, &c, 1)) {
                        str[j] = c;
                        j++;
                        if (c == ' ')
                            break;
                        if (c == '\n') {
                            line++;
                            break;
                        }
                    }
                }

                bytesread = read(fd, &c, 1);
                str[j] = c;
                j++;

                if ((c == ' ' || c == '\n') && flag == 0 && i == len) {
                    found++;
                    foundflag = 0;
                    printf("w was found in line %d.\n", line);
                }

                if ((c == '\n')&&(foundflag == 0)) {

                    for (j = 0; str[j] != '\n'; j += 5) {
                        printf("%c", str[j]);

                        if (str[j + 1] != '\n')
                            printf("%c", str[j + 1]);
                        else {
                            j++;
                            break;
                        }

                        if (str[j + 2] != '\n')
                            printf("%c", str[j + 2]);
                        else {
                            j += 2;
                            break;
                        }

                        if (str[j + 3] != '\n')
                            printf("%c", str[j + 3]);
                        else {
                            j += 3;
                            break;
                        }

                        if (str[j + 4] != '\n')
                            printf("%c", str[j + 4]);
                        else {
                            j += 4;
                            break;
                        }
                    }

                    for (; str[j] != '\n'; j++)
                        printf("%c", str[j]);

                    printf("\n");
                    j = 0;

                } else if (c == '\n')
                    foundflag = 1;

            } //end of while
            printf("w has occured %d times.\n", found);

        } else //end of if 2
            printf("couldn't read file.\n");

    } else //end of if 1
        printf("Couldn't open file for read.\n");

    close(fd);
} //end of main

这是终端中的输出:

w was found in line 1.
hello
w was found in line 2.
w was found in line 6.
hello world
hellooooo
w has occured 3 times.

这里是“hello.txt”的内容:

hello
hello world
hallo
I'm here
we're here
hello
hellooooo

输出中打印的行数是 1,2 和 6,但输出应该是这样的:

w was found in line 1.
hello
w was found in line 2.
hello world
w was found in line 6.
hello
w has occured 3 times.

【问题讨论】:

  • TL;博士!请将代码缩小到您遇到问题的部分。或者更好的是,请发送Minimal, Complete, and Verifiable Example 向我们展示。
  • 你有什么理由不使用strncmp来测试w的出现吗?
  • 但在我做过阅读的部分中,我几乎立即看到了一些未定义的行为。您将数组w 视为字符串,但您似乎忘记了C 中的字符串是终止的,而数组w 没有此终止符,因此当您例如致电strlen 它将超出范围,您将拥有 UB。
  • 使用char w[]="Hello"; 而不是char w[]={'h','e','l','l','o'};。这更具可读性并且(在您的情况下最重要),它添加了一个零终止符。见之前的 cmets。
  • 你忘了问问题。你的问题是“你如何调试 C 代码?”

标签: c printf output system-calls


【解决方案1】:
  1. 我建议你阅读一些 C 材料。您的代码表明您对该语言还不太了解。
  2. 我不会更改您的代码,因为那样会很难。
  3. 我将发布我的代码的相关部分并解释这些位。

所以,代码位:

const char fname[] = "hello.txt";
const char w[] = "hello";

(...)    

while (read(fd, &buffer[i], 1) == 1) {
    /* end of line */
    if (buffer[i] == '\n' || buffer[i] == 0x0) {
        buffer[i] = 0;
        if (!strncmp(buffer, w, strlen(w))) {
            printf("w was found in line %d\n", line);
            puts(buffer);
            n++;
        }
        line++;
        i = 0;
        continue;
    }
    i++;
}

说明

  1. while (read(fd, &amp;buffer[i], 1) == 1): 这将从您的fd 中读取一个字符(由之前的open 调用返回)并将其存储在缓冲区[i] 中。这里需要注意的相关事项是,在此之前您应该声明 int i = 0 并确保 buffer 是定义的数组或 malloced 内存区域。这个while 将一直持续到读取的字节数不同于 1(这是我们要求的)。

  2. if (buffer[i] == '\n' || buffer[i] == 0x0):这个if 检测到行尾。非常直接。

  3. buffer[i] = 0;if (!strncmp(buffer, w, strlen(w)))buffer[i] = 0 会将当前缓冲区的最后一个字符设置为零。它的作用是摆脱了我们读到的最后一个\n,所以我们可以用puts很好地打印它。我在 cmets 中建议的一点是使用 strncmp。这个函数就像strcmp,但它只会比较最多定义的字节数。因此,使用此函数,您可以有效地确定字符串是否以您要查找的子字符串开头。如果找到这个字符串,我们打印它所在的行,打印缓冲区本身并增加n,这是我们找到w 的次数的计数器。你应该在代码开头声明int n = 0;...

  4. line++; i = 0; continue;:这是在行尾检测if内。所以,它的作用是增加我们的行计数器,将i 设置为零——这很重要,因为在新的一行我们将读取一个新的缓冲区,并且该缓冲区索引必须从 0 开始。而continue强制循环重复而不执行其余代码。

  5. 最后,while 范围的其余部分定义为 i++。由于我们的 while 循环在每个字符处执行,缓冲区索引必须在读取每个字符后递增。


我测试的文件是您提供的文件。我得到的输出是:

w was found in line 1
hello
w was found in line 2
hello world
w was found in line 6
hello
w was found 3 times

【讨论】:

  • buffer[i] = 0x0 是什么意思?
  • @Raghad 在第一个if 条件中,它检查读取的字符是否为空字符。在if 范围 内,它将最后一个字符设置为空终止字节——这样,我们有效地删除了读入buffer[i]\n
  • 我如何知道我要查找的单词是在缓冲区的中间还是末尾?
  • @Raghad 您必须在每个部分中逐个字符地解析行字符 strncmp 才能找到。此版本仅在字符串开头查找匹配项。
  • @Raghad 不要只在换行符之后测试单词的并发性,而是保留一个您已经阅读过的内容的缓冲区,然后与该缓冲区进行比较。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-07
相关资源
最近更新 更多