【问题标题】:Reading from a file and setting an offset?从文件中读取并设置偏移量?
【发布时间】:2015-07-17 11:29:45
【问题描述】:

我是 C 新手,我正在尝试构建一个 C 程序,它扫描文件直到 EOF,挑选出包含某个关键字的行,然后在搜索最后一行后设置偏移量。再次执行扫描时,它会扫描文件,这次从保存的偏移量开始,一直向下直到 EOF。

我正试图围绕文件 I/O 的不同功能展开思考,但我无法将调用 fopen()、fseek()、fgets()、ftell() 等的过程拼凑起来我想要它做什么。谁能指出我正确的方向或指导我完成这项工作所需的内容?

谢谢!

【问题讨论】:

  • 您的意思是要“暂停”阅读吗?

标签: c fopen fgets fseek ftell


【解决方案1】:

在您的情况下,我建议使用 getline 进行阅读,使用 ftellfseek 获取/设置偏移量(以及使用 strstr 搜索单个行)。

我不确定我是否理解您保存偏移量的全部内容,但它可能看起来像这样:

int pick_lines(const char *filename, const char *keyword, long *offset)
{
    FILE *fp;
    char *line = NULL;
    size_t len = 0;

    if (offset == NULL || (fp = fopen(filename, "r")) == NULL)
        return 1;

    if (*offset > 0 && fseek(fp, *offset, SEEK_SET) != 0) {
        fclose(fp);
        return 1;
    }

    while (getline(&line, &len, fp) != -1) {
        if (strstr(line, keyword) != NULL)
            printf("%s", line); // or do something else with chosen line
    }

    if ((*offset = ftell(fp)) < 0) {
        free(line);
        fclose(fp);
        return 1;
    }

    free(line);
    fclose(fp);
    return 0;
}

这里offset 是一个输入/输出参数。它的取消引用值用于寻找给定的偏移量(以*offset == 0 开头),然后重置为新的偏移量。

这个函数只会打印包含keyword 的每一行。如果您想返回一个行数组,则需要做一些额外的工作。

一个使用示例可能是:

long offset = 0;
pick_lines(filename, keyword, &offset);
// append lines to file
pick_lines(filename, keyword, &offset);
// ...

【讨论】:

  • *offset = ftell(fp) 设置为EOF 位置。 return 1;之前还需要free & fclose
  • if ((*offset = ftell(fp)) &lt; 0) { .. } 这个错误的位置。因为 while 结束意味着文件结束。
  • 是的,*offset 将被设置为 EOF 位置,这就是重点。为什么会出错?
  • 挑选出包含某个关键字的行,然后在搜索到最后一行后设置偏移量。 但你总是设置EOF 位置。您的示例的第二次调用毫无意义。
  • “您的示例的第二次调用毫无意义”:不,不是,如果您在两次调用之间附加到文件中,因为我的评论 // append lines to file 是为了建议 (FILE *fp = fopen(filename, "a"); fprintf(fp, /*additional lines*/); fclose(fp);)。当我这样做时,它会打印包含 keyword 的添加行,而不搜索前面的行。
【解决方案2】:

你可以这样做(只是伪代码):

fopen();
offset = loadOffset();
fseek(offset); // set offset from previous run
while(!feof())
{
  fgets();
  if(searchKeyword() == true)
  {
    offset = ftell(); // getting the offset (after the line you just read)
    doSomething();

  }
}
saveOffset(offset);
fclose();

提示:小心使用 feof();仅当输入操作因 EOF 而失败时,它才返回 true。如果文件指针位于 EOF 但之前没有失败,则返回 false。你必须处理那个案子。

【讨论】:

  • 小心使用 feof()
【解决方案3】:

听起来您想要做的是以“标题”开始文件,该标题定义了找到最后一个结果的位置。这样,该信息被写入并存储在文件本身中。一个 8 位十六进制值可能足以表示最大为 4GB 的文件中的偏移量。比如:

00000022<cr><lf>
Text...<cr><lf>
More text...<cr><lf>
~ <cr><lf>  <-- this '~' is whatever we're looking for
Other stuff...<cr><lf>

我在这里做了一些假设。首先,这是在 Windows 上,其中文本行以 &lt;cr&gt;&lt;lf&gt; 字符(分别为 0x0D 和 0x0A)终止。如果是 Unix,它将仅是 &lt;lf&gt;。如果是 Mac,它可能只是 &lt;cr&gt;,或者其他任何一个。我在这个例子中计算了它们。这是假设 ANSI 样式的字符串,这意味着 8 位编码(一个字符 = 一个字节的数据。)使用 Unicode 或其他字符串格式可以实现相同的功能,请注意它们可能不再是每个字节特点。 (在 Unicode 中,每个字符是两个字节。因此,如果混合 Unicode 和 ANSI 字符串操作,可能会遇到麻烦。)

这里,“header”的值为 0x22 或十进制的 34,如果从文件开头开始计算所有字符,则在第 34 个计数处达到“~”。所以“标题”指向最后一个搜索结果的位置。

它的工作原理是这样的:最初这个头值是零,所以你的代码会读到它并且知道它还没有被搜索过。假设代码扫描文件,每个字符加一,直到找到“~”字符。然后它回到开头,将此计数值转换为 8 个文本字符(itoasprintf),并用它覆盖文件的这一部分。一个人再次找到、完成或处理整个事情以搜索更多。现在下一次处理该文件时,您的代码会读取此标头值,并将其从文本转换为 uint (atoi),将文件查找到此偏移加一(因为我们不想再抓到这个了),然后重新开始扫描。

这里的其他人有一些很好的实际代码示例可供开始试验。请注意,如果您要查找的不仅仅是一个字符,例如一个单词或一系列数字,则扫描部分会变得更慢且更复杂。对“标记”而不是简单的字符或单词的复杂扫描称为词典分析,这是一个完全不同的主题。谷歌Flex and BisonYACC

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-29
    • 2012-04-28
    • 2012-03-29
    • 1970-01-01
    • 2018-07-03
    • 2012-10-20
    • 2018-03-27
    相关资源
    最近更新 更多