【发布时间】:2013-03-11 02:14:33
【问题描述】:
我正在打开一个文件并将其内容放入一个字符串缓冲区中,以便对每个字符进行一些词法分析。这样做可以比使用后续的 fread() 调用更快地完成解析,并且由于源文件将始终不大于几 MB,我可以放心,整个内容将始终读取文件。
但是,检测何时没有更多数据要解析似乎有些麻烦,因为 ftell() 经常给我一个大于文件中实际字符数的整数值.如果尾随字符始终为 -1,则使用 EOF (-1) 宏不会有问题……但情况并非总是如此……
这是我打开文件并将其读入字符串缓冲区的方式:
FILE *fp = NULL;
errno_t err = _wfopen_s(&fp, m_sourceFile, L"rb, ccs=UNICODE");
if(fp == NULL || err != 0) return FALSE;
if(fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
fp = NULL;
return FALSE;
}
LONG fileSize = ftell(fp);
if(fileSize == -1L) {
fclose(fp);
fp = NULL;
return FALSE;
}
rewind(fp);
LPSTR s = new char[fileSize];
RtlZeroMemory(s, sizeof(char) * fileSize);
DWORD dwBytesRead = 0;
if(fread(s, sizeof(char), fileSize, fp) != fileSize) {
fclose(fp);
fp = NULL;
return FALSE;
}
这似乎总是完美无缺。下面是一个简单的循环,它一次检查一个字符的字符串缓冲区的内容,如下所示:
char c = 0;
LONG nPos = 0;
while(c != EOF && nPos <= fileSize)
{
c = s[nPos];
// do something with 'c' here...
nPos++;
}
文件的尾随字节通常是一系列 ý (-3) 和 « (-85) 个字符,因此永远不会检测到 EOF。相反,循环只是继续前进,直到 nPos 最终具有比 fileSize 更高的值——这对于正确的词法分析来说是不可取的,因为您通常最终会跳过最后的流中的标记,在末尾省略换行符。
在基本拉丁字符集中,假设 EOF 字符是具有负值的任何字符是否安全?或者也许有更好的方法来解决这个问题?
#EDIT: 我刚刚尝试将 feof() 函数实现到我的循环中,但仍然没有似乎也无法检测到 EOF。
【问题讨论】:
-
阅读失败时(可能)会泄漏大量内存。您不允许在读取的字符串末尾使用空终止符。当内存即将被文件中的数据覆盖时,将内存归零是没有意义的。您的测试循环正在越界访问内存;
nPos == fileSize超出了您分配的内存的末尾。 -
您是否有理由标记此 C++,但使用在我看来更像纯 C 的东西?你试过 C++ 文件流吗?你的文件是什么编码的?
-
只有在尝试读取不存在的数据时才会检测到 EOF。
fread()不会报告 EOF;您要求阅读文件中的内容。如果你在fread()之后尝试getc(fp),你会得到EOF,除非文件在你测量它的长度后已经增长。由于_wfopen_s()是一个非标准函数,它可能会影响ftell()的行为方式及其报告的值。 没有;假设任何负 char 值都是 EOF 是不安全的。普通类型char可以是有符号或无符号的。 -
@WarrenP:由于
new[fileSize],它被正确标记为 C++。它可能不是惯用的 C++,但绝对不是 C。 -
EOF 未在缓冲区中编码。它是从
fgetc()orfgetwc()返回的,具体取决于您处理文件本身的方式,并且与您的方式无关。但是您以二进制模式打开文件,老实说,我什至不知道它是 supported 与ccs编码模式。如果您使用计算文件长度+1(终止符的+1),则缓冲区的大小应以字节为单位适当。如果以二进制模式打开并指定编码提示以请求 BOM 分析,那就更好了。