使用fgets函数返回值而不是feof

本文原来发表在CSDN博客上面,CSDN上内容现在已迁出至github page

今天在调试程序的时候,发现vector中多了一个数据。函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void create_testcase::read_districtfiles()
{
tstring::size_type pos=m_str_logpath.rfind(_T('//'));
if (tstring::npos==pos) return;
tstring str_name = m_str_logpath+m_str_logpath.substr(pos);
FILE *pinfile = _tfopen(str_name.c_str(), _T("rt+, ccs=UTF-8"));
if (NULL==pinfile) return;
TCHAR linex[MAX_PATH]=_T("");
tstring str_line, str_district, str_count;
while (!feof(pinfile))
{
_fgetts(linex, MAX_PATH, pinfile);
str_line = tstring(linex);
...;
m_name_vector.push_back(tspair(_ttoi(str_count.c_str()), str_district));
}
fclose(pinfile);
}

加断点看看,最后一个元素重复了,再追,发现原来是文件最后一行被fgets()函数读了两遍。

查MSDN,feof,有这么一段话:
“The feof routine (implemented both as a function and as a macro) determines whether the end of stream has been passed. When the end of file is passed, read operations return an end-of-file indicator until the stream is closed or until rewind, fsetpos, fseek, or clearerr is called against it.

For example, if a file contains 10 bytes and you read 10 bytes from the file, feof will return 0 because, even though the file pointer is at the end of the file, you have not attempted to read beyond the end. Only after you try to read an 11th byte will feof return a nonzero value.”

注意,上面说的是“the end of stream has been passed, read operations return an end-of-file indicator”,也就是说,只有文件位置指针越过文件结尾以后,read操作才会返回e-o-f的指示符。

测试1. 创建一个空的txt文件,并使用下面程序测试:

1
2
3
4
5
6
7
8
9
10
void TestFun1()
{
FILE *pinfile = _tfopen(_T("f://12.txt"), _T("rt+"));
if (NULL==pinfile) return;
while (!feof(pinfile))
{
MessageBox(NULL, _T("test"), _T("debug"), 0);
}
fclose(pinfile);
}

结果:啊哦,进入死循环了。
分析:文件位置指针一直没有越过文件结尾,feof一直返回为0,所以进入死循环。

测试2. 程序更改为:

1
2
3
4
5
6
7
8
9
10
11
12
void TestFun2()
{
FILE *pinfile = _tfopen(_T("f://12.txt"), _T("rt+"));
if (NULL==pinfile) return;
TCHAR szTest[MAX_PATH]=_T("");
while (!feof(pinfile))
{
_fgetts(szTest, MAX_PATH, pinfile);
MessageBox(NULL, szTest, _T("debug"), 0);
}
fclose(pinfile);
}

结果:弹出一个对话框,内容为空。
分析:fgets函数读MAX_PATH-1个字符以后,文件位置指针已经后移,并且越过了文件结尾,所以下一次调用feof时,返回非0值。

测试3. 在txt文件中添加数字“1”,TestFun2()函数测试。
结果:只弹出一个对话框,内容为1。
分析:fgets函数读MAX_PATH-1个字符以后,文件位置指针已经后移,并且越过了文件结尾,所以下一次调用feof时,返回非0值。

测试4. 在txt文件中1后面加上回车符,TestFun2()函数测试。
结果:弹出两个对话框,内容都为1。
分析:fgets第一次,遇到换行符,停止,此时文件位置指针已经到达文件尾部,但是还没有越过,所以,while没有退出, 因为文件位置指针已经到文件尾部,所以第二次fgets没有读到任何数据。

测试5. 文件内容不变,使用下面程序测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void TestFun3()
{
FILE *pinfile = _tfopen(_T("f://12.txt"), _T("rt+"));
if (NULL==pinfile) return;
TCHAR szTest[MAX_PATH]=_T("");
while (!feof(pinfile))
{
if (NULL!=_fgetts(szTest, MAX_PATH, pinfile))
MessageBox(NULL, szTest, _T("debug"), 0);
else
MessageBox(NULL, _T("NULL"), _T("debug"), 0);
}

fclose(pinfile);
}

结果:弹出两个对话框,内容分别为“1”和“NULL”。
分析:以上结果与测试4的分析是一致的。

测试6. 文件内容不变,使用下面程序测试

1
2
3
4
5
6
7
8
9
10
11
12
void TestFun4()
{
FILE *pinfile = _tfopen(_T("f://12.txt"), _T("rt+"));
if (NULL==pinfile) return;
TCHAR szTest[MAX_PATH]=_T("");
while (NULL!=_fgetts(szTest, MAX_PATH, pinfile))
{
MessageBox(NULL, szTest, _T("debug"), 0);
}
fclose(pinfile);

}

结果:只弹出一个对话框,内容为“1”。

总结:所以,我们在整行得去文件的时候,最好采用fgets函数的返回值来控制while循环而不是采用fefo函数。