'\n'
被替换成了"\r\n"
,我之前并不知道。(我猜Mac上也会被替换...)
有没有一种简单的方法可以确保Linux、Mac和Windows用户可以轻松地交换文本文件?
所谓简单的方法是指:不用以二进制模式写入文件或自己(或使用第三方程序/代码)测试和替换行尾字符。这个问题影响了我做文本文件I/O的C++程序。'\n'
被替换成了"\r\n"
,我之前并不知道。(我猜Mac上也会被替换...)
有没有一种简单的方法可以确保Linux、Mac和Windows用户可以轻松地交换文本文件?
所谓简单的方法是指:不用以二进制模式写入文件或自己(或使用第三方程序/代码)测试和替换行尾字符。这个问题影响了我做文本文件I/O的C++程序。抱歉部分重复其他答案,但为了完整起见:
谬论: endl
是“更便携”的,因为它会根据平台惯例写入换行符。
真相: endl
被定义为向流中写入 \n
,然后调用 flush
。所以实际上你几乎不想使用它。所有写入文本模式流的 \n
都会被 CRT 在后台隐式转换成 \r\n
,无论你使用 os<<endl
、os<<'\n'
还是 fputs("\n",file)
。
谬论:你应该在文本模式下打开文件来写文本,在二进制模式下打开文件来写二进制数据。
真相: 文本模式之所以存在,是因为以前有一些文件系统区分文本文件和二进制文件。在我知道的任何健全的平台上,它已经不再成立了。你同样可以将文本写入以二进制方式打开的文件中,只是在 Windows 上你会失去自动的 \n
到 \r\n
的转换。然而,这种转换会带来更多的麻烦。其中之一是它使你的代码在不同的平台上表现不同,并且 tell/seek
变得难以使用。因此最好避免这种自动转换。请注意,POSIX 不区分二进制模式和文本模式。
如何进行文本处理:用二进制模式打开所有内容,仅使用普通的 \n
即可。您还需要关注编码。为了正确处理 Unicode,请统一使用 UTF-8。内部使用 UTF-8 编码的窄字符串 ,而不是在不同平台上有所不同的 wchar_t
。您的代码将变得更容易移植。
提示:你可以通过以下方式强制 MSVC 默认以二进制模式打开所有文件。应该按照以下步骤操作:
#include <stdio.h>
#include <iostream>
int main() {
_fmode = _O_BINARY;
std::ofstream f("a.txt"); // opens in binary mode
}
编辑:自2021年起,Windows 10的记事本支持UNIX换行符。
fopen
,它只是最简单和最明确的例子。你可能会更喜欢编辑过的版本。 - Yakov Galka'\n'
会产生 Unix 行尾符。在 Windows 上,这会破坏像记事本这样的烂文本编辑器以及你将这些内容粘贴到的几乎所有文本框(即使是从处理 Unix 行尾符的编辑器中复制的)。这真的是你所倡导的吗?还是我完全误解了你的意思? - Marcelo Cantosfgets()
会将 CR 读入缓冲区,这会导致取决于输入文件中行尾的不同行为。是否重要取决于程序员的意图;它不应受到您建议的不可谈判规则的约束。关于我的“声明”:大多数 C 程序(特别是可移植的程序)使用 fopen()
,在 Windows 上会对文本和二进制文件进行不同的处理(至少在我使用的每个运行时库上都是如此)。 - Marcelo Cantos问题并不在于endl
,而是文本流根据系统标准重新格式化换行符。
如果您不想这样做,只需不使用文本流-使用二进制流。也就是说,用ios::binary
标志打开您的文件。
话虽如此,如果唯一的问题是用户可以交换文件,我将不会再费心考虑输出模式,相反,我会确保您的程序能够在不崩溃的情况下接受不同的格式,也就是说,它应该接受不同的行尾。
顺便说一句,任何像样的文本编辑器都会这样做(但是Windows上的默认notepad.exe
不是一个像样的文本编辑器,并且无法正确处理Unix行尾)。
如果你真的只想要一个ASCII换行符,最简单的方法是以二进制模式打开文件:在非二进制模式下,\n会被替换为特定平台的行尾序列(例如,它可能被替换为LF/CR或CR/LF序列;在Unix系统上通常只是LF)。在二进制模式下,不会进行这种替换。关闭替换也是二进制模式唯一的作用。
顺便说一句,使用endl等价于写入一个\n并刷新流。常常意外的刷新可能会导致严重的性能问题。因此,应该很少使用endl,并且仅在需要刷新时使用。