fgetpos()行为取决于换行符

时间:2013-03-26 23:08:10

标签: c windows file newline

考虑这两个文件:

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的问题。

新行序列如何影响文件的内部位置,即使在遇到该字符之前也是如此?

2 个答案:

答案 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()个字节。