我有用于处理开头有数字的文件的代码,我必须读取该文件,然后是字母。我需要打印带有与文件开头的数字相对应的索引的字母,其中索引被解释为从文件开头开始的位置。 例如,如果文件中的第一个数字是 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"
整个文件永远不会比最大索引短。
你的程序在 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 上按预期工作(例如),但不一定在其他一些系统上工作。
一种解决方案是:
if ((fp = fopen("chance.txt", "rb")) == NULL) {
(注意打开模式 "rb"
而不是
"r"
)。
代码中的问题与 fseek 和 ftell 与文件位置指示器的结合使用有关。当您使用 fseek(fp, num, SEEK_SET); 时,它将文件位置指示器设置为从文件开头算起的绝对位置 num。但是,当您稍后使用 ftell(fp) 获取当前文件位置时,它会返回从文件开头到当前位置的偏移量。这意味着 where 不是绝对位置而是偏移量,从而导致意外的行为。
#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 正确地为您提供从文件开头开始的绝对位置。