翻译《有关编程、重构及其他的终极问题?》——21.正确的检查文件的结尾符(EOF)
标签(空格分隔): 翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
最后更新:2017年03月01日
21.正确的检查文件的结尾符(EOF)
让我们继续文件处理的话题,深入了解一下EOF。但这次我们会说说关于完全不同类型造成的bug,这通常会在部本地化版本的软件中发现。
下面这段代码来自Computational Network Toolkit。PVS-Studio诊断的错误说明为:V739 EOF should not be compared with a value of the ‘char’ type. The ‘c’ should be of the ‘int’ type(译者注:大意是EOF不能和char类型的值比较,应该和int类型比较)。
string fgetstring(FILE* f)
{
string res;
for (;;)
{
char c = (char) fgetc(f);
if (c == EOF)
RuntimeError("error reading .... 0: %s", strerror(errno));
if (c == 0)
break;
res.push_back(c);
}
return res;
}
解释
让我们看看EOF是如何被声明的:
#define EOF (-1)
如你所见,EOF只不过是一个值为-1的整型。fgetc()函数返回一个整型的值,也就是说,它可以返回值为0到255(或-1)的数字。在上面的代码中这个值被转换为char类型,因此值为0xFF(255)就转为值为-1了(译者注:如果不明白,回去复习基础的二进制表达正数、负数的基本知识),所以-1也就作为了文件的结尾符进行处理(EOF)。
如果用户使用了扩展的ASCII码,当程序没有正确处理字母表中某一个符号时,也就会有遇到一个错误。
比如Windows 1251 code页,在俄文字母表中的最后一个字母值就是0xFF,因此,就被程序解释为文件的结尾符号。
正确的代码
for (;;)
{
int c = fgetc(f);
if (c == EOF)
RuntimeError("error reading .... 0: %s", strerror(errno));
if (c == 0)
break;
res.push_back(static_cast<char>(c));
}
建议
其实我这里没有特别的建议,至于谈到EOF,我只是想展示一个比较有趣的错误的变化,因为部分人可能从来没有意识到。
记住,如果函数返回的是整型类型,不要马上把它转换为char类型,请停下来并确认相关代码都没有问题。顺便说一下,我们其实已经谈到过类似的问题,即在第二篇中讨论的memcmp()函数——“比0大的并不意味着就只是1”(其中的代码涉及到了MySQL)