考虑这两个文件:
file1.txt(Windows换行符)
abc\r\n
def\r\n
file2.txt(Unix换行符)
abc\n
def\n
我注意到对于file2.txt,使用fgetpos
获得的位置没有正确递增。我正在使用Windows。
让我举个例子。以下代码:
#include<cstdio>
void read(FILE *file)
{
int c = fgetc(file);
printf("%c (%d)\n", (char)c, c);
fpos_t pos;
fgetpos(file, &pos); // save the position
c = fgetc(file);
printf("%c (%d)\n", (char)c, c);
fsetpos(file, &pos); // restore the position - should point to previous
c = fgetc(file); // character, which is not the case for file2.txt
printf("%c (%d)\n", (char)c, c);
c = fgetc(file);
printf("%c (%d)\n", (char)c, c);
}
int main()
{
FILE *file = fopen("file1.txt", "r");
printf("file1:\n");
read(file);
fclose(file);
file = fopen("file2.txt", "r");
printf("\n\nfile2:\n");
read(file);
fclose(file);
return 0;
}
给出了这样的结果:
file1:
a (97)
b (98)
b (98)
c (99)
file2:
a (97)
b (98)
(-1)
(-1)
file1.txt按预期工作,而file2.txt表现很奇怪。 为了解释它有什么问题,我尝试了以下代码:
void read(FILE *file)
{
int c;
fpos_t pos;
while (1)
{
fgetpos(file, &pos);
printf("pos: %d ", (int)pos);
c = fgetc(file);
if (c == EOF) break;
printf("c: %c (%d)\n", (char)c, c);
}
}
int main()
{
FILE *file = fopen("file1.txt", "r");
printf("file1:\n");
read(file);
fclose(file);
file = fopen("file2.txt", "r");
printf("\n\nfile2:\n");
read(file);
fclose(file);
return 0;
}
我得到了这个输出:
file1:
pos: 0 c: a (97)
pos: 1 c: b (98)
pos: 2 c: c (99)
pos: 3 c:
(10)
pos: 5 c: d (100)
pos: 6 c: e (101)
pos: 7 c: f (102)
pos: 8 c:
(10)
pos: 10
file2:
pos: 0 c: a (97) // something is going wrong here...
pos: -1 c: b (98)
pos: 0 c: c (99)
pos: 1 c:
(10)
pos: 3 c: d (100)
pos: 4 c: e (101)
pos: 5 c: f (102)
pos: 6 c:
(10)
pos: 8
我知道fpos_t
并不是由编码员解释,因为它取决于实现。但是,上面的示例解释了fgetpos
/ fsetpos
的问题。
新行序列如何影响文件的内部位置,即使在遇到该字符之前也是如此?
答案 0 :(得分:3)
我想说这个问题可能是因为第二个文件混淆了实现,因为它是在文本模式下打开的,但它不符合要求。
在标准中,
文本流是由字符组成的有序字符序列 行,每行由零个或多个字符加上a组成 终止换行符
您的第二个文件流不包含有效的换行符(因为它会在内部查找\r\n
转换为换行符)。因此,实现可能无法正确理解行长度,并且当您尝试在其中移动时会毫无希望地混淆。
此外,
可能必须在输入和/或添加,更改或删除字符 输出符合表示文本的不同约定 主机环境。
请记住,当您调用fgetc
时,库不会只读取文件中的每个字节 - 它会将整个文件(对于一个如此小的文件)读入流的缓冲区并对其进行操作。
答案 1 :(得分:2)
我将其添加为teppic's answer的支持信息:
当处理已作为文本而不是二进制文件打开的FILE*
时,VC ++ 11(VS 2012)中的fgetpos()
函数可能(并且适用于您的file2.txt
示例)结束在这段代码中:
// ...
if (_osfile(fd) & FTEXT) {
/* (1) If we're not at eof, simply copy _bufsiz
onto rdcnt to get the # of untranslated
chars read. (2) If we're at eof, we must
look through the buffer expanding the '\n'
chars one at a time. */
// ...
if (_lseeki64(fd, 0i64, SEEK_END) == filepos) {
max = stream->_base + rdcnt;
for (p = stream->_base; p < max; p++)
if (*p == '\n') // <---
/* adjust for '\r' */ // <---
rdcnt++; // <---
// ...
它假定缓冲区中的任何\n
字符最初是\r\n
序列,当数据被读入缓冲区时,该序列已被规范化。因此,有时它会尝试考虑它认为文件的先前处理已从缓冲区中删除的(现在缺少的)\r
字符。当你接近文件的末尾时会发生这种特殊的调整;但是,还有其他类似的调整可以解决\r
处理中已删除的fgetpos()
个字节。