最近用C++读取文件时遇到了一个问题,读取如下图所示的文件时,使用C中的feof()判断是否读取结束,总会在文件末尾处总会多读一遍。
可以看到我读取的文件格式是比较简单的,没两行为一对,第一行有两个int类型的数字,第二行是一个字符串,包含图像的名称。
先上我读取文件的代码:
void read(string name)
{
FILE *file = fopen(name.c_str(), "r");
while (!feof(file))
{
int i, j;
fscanf(file, "%d%d\n", &i, &j);
char filename[100];
fgets(filename, 100, file);
filename[strlen(filename) - 1] = '\0';
}
}
可以看到,读取的代码非常简单,但是这样会出现一个问题,最后一行会重复输出一遍。
造成这个问题的原因在于feof在真正文件读取结束后还会继续在判断一遍才会结束。
为了解决这个问题,有人是使用了如下方法
char c;
c = fgetc(fp);
while(!feof(fp))
{
printf("%X/n", c);
c = fgetc(fp);
}
printf("%X/n", c);
c = fgetc(fp);
}
这种方法确实可以避免重复读取最后一行的问题,但是这种方法更加适合于每次读取单个字符的情况,对于我遇到的这种,需要读取int和字符串的情况就不是很适用,最终我在网上找到了一种万无一失的方法解决feof的问题,那就是不用它。。。
我们适用fseek和ftell来配合判断是否读取到文件末尾。
void read(string name)
{
FILE *file = fopen(name.c_str(), "r");
fseek(file, 0L, SEEK_END);
long end = ftell(file);
fseek(file, 0L, SEEK_SET);
long start = ftell(file);
while (end != start)
{
int i, j;
fscanf(file, "%d%d\n", &i, &j);
char filename[100];
fgets(filename, 100, file);
if (filename[strlen(filename) - 1] == '\n')
filename[strlen(filename) - 1] = '\0';
cout << filename << endl;
start = ftell(file);
}
}
大体思路就是首先通过fseek得到文件末尾的位置,然后在读取文件以后不断计算当前读取的文件位置,如果读取后发现当前位置与文件末尾位置相同,则停止继续读取文件。
这种方法的好处是万金油,记住这一种,以后遇到所有需要读取全部文件的情况都可以适用。简单易懂。
当然利用C++的方法也可以实现这一需求,但是有些情况下C++的读写速度明显低于C语言,而且C++兼容C语言。