fscanf 将文件位置更改为负值

问题描述 投票:0回答:2

我有用于处理开头有数字的文件的代码,我必须读取该文件,然后是字母。我需要打印带有与文件开头的数字相对应的索引的字母,其中索引被解释为从文件开头开始的位置。 例如,如果文件中的第一个数字是 10,那么我需要打印文件的第十个字符。

问题是,当我使用

fscanf
时,第一次使用后,
ftell
给出的负索引正好是 -7(其中 的值为 -7)。这使得程序停止处理该字母,因为尝试使用
scanf
读取该字母返回 0。为什么?

#include <stdio.h>

int main() {
    char letter;
    int num;
    long where;
    FILE *fp;
    if ((fp = fopen("chance.txt","r")) == NULL) {
        printf("File corrupted");
        return 1;
    }

    rewind(fp);
    while (fscanf(fp,"%d",&num) == 1) {
        where = ftell(fp);
        fseek(fp,num,SEEK_SET);
        letter = (char)fgetc(fp);
        printf("%c", letter);
        fseek(fp,where,SEEK_SET);
    }
    fclose(fp);
    return 0;
}

文件可能看起来像这样

30 23 42
32
"Some text"

整个文件永远不会比最大索引短。

c file scanf ftell
2个回答
0
投票

你的程序在 Linux 上为我干净地编译,未经修改,并且它的行为完全按照你所描述的那样。但是,我无法使用问题中提供的输入文件进行测试,因为它不满足文件长度至少与其中最大索引一样大的要求。 (实际上,长度需要超过最大索引,即使只有 1 个字节,因为当程序使用它们时,索引是从零开始的。)

如果在您的环境中,程序在处理有效输入文件时出现错误,则很可能是由于您将文件作为文本流打开,但以未完全定义的方式使用文件定位函数对于文本流。 POSIX 系统不区分文本流和二进制流,但其他一些系统会区分,Windows 就是一个很好的例子。

考虑这段代码:

        where = ftell(fp);
        fseek(fp,num,SEEK_SET);
        letter = (char)fgetc(fp);
        printf("%c", letter);
        fseek(fp,where,SEEK_SET);

鉴于

fp
指的是文本流,
ftell
很好,
fseek(fp,where,SEEK_SET)
也很好:只要流首先是可查找的,您可以使用
fseek
将其重新定位到之前通过
ftell()
记录的点。然而,C 并没有定义在文本流中寻找任意位置的含义,所以
fseek(fp,num,SEEK_SET)
不太好。这将在 Linux 上按预期工作(例如),但不一定在其他一些系统上工作。

一种解决方案是:

  1. 以二进制流方式打开文件:

    if ((fp = fopen("chance.txt", "rb")) == NULL) {
    (注意打开模式 

    "rb"

     而不是 
    "r"
    )。

  2. 确保文件具有单字节行终止符(至少如果它应该在 Windows 上产生与 Linux 上相同的输出)。

此外,尽管看起来很乏味,但明智的做法是检查所有可能指示错误的函数调用的返回值,以便在错误信号出现时识别它们,并采取适当的措施。通常,这只是报告错误的性质及其位置,然后终止。你错过了一些。我不确定检查这些是否会给您提供有用的信息,但它可能已经做到了。

代码中的问题与 fseek 和 ftell 与文件位置指示器的结合使用有关。当您使用 fseek(fp, num, SEEK_SET); 时,它将文件位置指示器设置为从文件开头算起的绝对位置 num。但是,当您稍后使用 ftell(fp) 获取当前文件位置时,它会返回从文件开头到当前位置的偏移量。这意味着 where 不是绝对位置而是偏移量,从而导致意外的行为。

-2
投票
这是代码的修改版本,应该可以正常工作:

#include <stdio.h> int main() { char letter; int num; FILE *fp; if((fp = fopen("chance.txt", "r")) == NULL) { printf("File corrupted"); return 1; } rewind(fp); while (fscanf(fp, "%d", &num) == 1) { fseek(fp, num, SEEK_SET); letter = (char)fgetc(fp); printf("%c", letter); fseek(fp, 0, SEEK_SET); // Reset the file position to the beginning } fclose(fp); return 0;

}
在这段修改后的代码中,读取数字并使用 fseek 移动到指定位置后,我添加了 fseek(fp, 0, SEEK_SET);在下一次迭代之前将文件位置重置为文件的开头。这样,您可以确保后续的 ftell 正确地为您提供从文件开头开始的绝对位置。

© www.soinside.com 2019 - 2024. All rights reserved.