使用 fgetc 读取换行符但在 macOS 中出错

use fgetc reading newline character but error in macOS

我的代码需要用到很多fgetc(inp).

在windows下没有问题,但在macOS下程序会报错

我发现问题是由于两个系统的换行符字符数不一致导致的: macOS 只是 \n,windows 是 \r\n

所以我创建了一个新函数来替换 fgetc(inp) 读取换行符

void getwhite() {
    int white = fgetc(inp);
    if (isspace(white) == 0) {
        fseek(inp, -1, SEEK_CUR);
    }
}

但是没有按预期工作,在 windows 中仍然工作正常,macOS 仍然报错

您应该使用 ungetc() 而不是 fseek() 来推回从流中读取的字节:

int getwhite() {
    int c = fgetc(inp);
    if (!isspace(c)) {
        ungetc(c, inp);
    }
    return c;
}

关于 windows 和其他系统上的行尾处理:由于遗留原因,windows 仍然使用 CR LR 序列来指示文本文件中的行尾,C 库翻译这些序列透明地 到单个 '\n' 字节,用于将文件作为文本读取的程序,使用 fopen() 或更低级别的 open() 接口。

这使得文件偏移量难以使用,因为从文件中读取的字节数可能与文件中的字节偏移量不同,而标准函数无法检索到:long ftell() 对于以文本模式打开的流,仅作为要传递给 fseek() 对于以文本模式打开的同一文件的 SEEK_SET 模式的数字才有意义。在文本流上的 SEEK_CURSEEK_END 模式下使用非零偏移量进行搜索具有未定义的行为,如 C 标准中所指定:

7.21.9.2 The fseek function
Synopsis

#include 
int fseek(FILE *stream, long int offset, int whence);

Description
[...]

For a text stream, either offset shall be zero, or offset shall be a value returned by an earlier successful call to the ftell function on a stream associated with the same file and whence shall be SEEK_SET.

如果您需要依赖文件偏移量,您应该以二进制模式打开文件并在您自己的代码中明确处理行结尾。

Apple 操作系统曾经将行尾表示为单个 CR 字节,但在 10 多年前采用了 Mach unix 兼容内核时切换为单个 NL 字节。