跨平台的换行符混乱问题

12

由于某些原因,我的写入文本文件的函数突然停止工作了。

void write_data(char* filename, char* writethis)
{
    ofstream myfile;
    myfile.open (filename, std::ios_base::app);
    myfile << endl << writethis;
    myfile.close();
}
该函数是在一个循环中被调用的,所以基本上它从一个空行开始,并将所有接下来的“writethis”行附加到新行上。
然后突然间,没有更多的换行符了。所有的文本都被附加在一个单独的行上。所以我进行了一些挖掘,发现了以下内容:
  1. Windows = CR LF
  2. Linux = LF
  3. MAC < 0SX = CR
因此,我把该行改为:
myfile << "\r\n" << writethis;

它又起作用了。但是现在我很困惑。我正在使用linux编程,但是在使用filezilla将程序生成的文本文件从windows传输出来后进行阅读。这其中哪一部分导致文本文件中的行呈现为一行呢?

我非常确定 "endl" 在linux上可以正常工作,所以现在我认为是windows在使用filezilla传输文件之后搞乱了文件?打乱文本文件的写入和读取方式将导致我的程序崩溃,所以如果有人能够解释这个问题,我将不胜感激。

我也不记得我在程序中做了什么改变导致它不能工作,因为先前它一直很好用。我添加的唯一一件事就是线程。

编辑: 我尝试了将传输模式从ASCII /二进制切换(甚至删除了txt扩展名的强制ASCII),但没有任何区别。换行符在linux上出现,但在windows上没有出现。 fz-messup

多奇怪啊。


3
重复的十亿个关于行结尾的问题。 - Lightness Races in Orbit
@TomalakGeret'kal,那为什么我的搜索结果中没有出现呢? - natli
因为你没有正确搜索? - Lightness Races in Orbit
可能是什么是\r和\n的区别?的重复问题。还请参考http://stackoverflow.com/q/728312/10468。 - DarenW
我不同意,我认为这些问题的范围非常不同。但我不会反对关闭它,我只是不同意理由。 - natli
1
每输出一行就重新打开和关闭文件是低效的。不过,函数体可以更简单地编写为 ofstream(filename, std::ios_base::app) << endl << writethis;。这充分利用了 RAII。此外,您应该使用 std::string 来表示文本项。 - Karl Knechtel
4个回答

11
发生的情况是,你写了Unix行结束符('\n'),然后将其转移到Windows机器上,得到一个完全相同的文件,然后尝试使用不理解Unix行结束符(可能是记事本)的查看器打开文件。
从我在编写可移植代码方面的经验来看:
- 在所有平台上都要统一使用一个行结束符('\n',LF)。 - 即使是写文本,也要始终以二进制方式打开文件。 - 让打开文件的用户使用能够理解任何行结束符的文本查看器。Windows有很多这样的软件(包括Visual Studio、Notepad++、Wordpad和您喜欢的浏览器)。
是的,我确实认为每个人都应该标准化为一个东西,而不是在任何地方支持所有东西。此外,我否认“适当平台上的正确行结束符”的存在。微软决定他们的本地API不支持UTF-8或不理解Unix行结束符并不妨碍Windows上的所有人的代码这样做。只要确保不将此类内容传递给WinAPI即可。很多时候,你会在内部数据上进行文本处理,系统永远不会看到它,那么你为什么要通过满足这些系统内部的期望来复杂化自己的生活呢?

1
这确实消除了问题,但是期望任意用户准备好这样做有点困难,并且通过绕过问题根本没有解决它。正确的方法是“修复文件传输”,以便在适当的平台上具有正确的行尾。而且好消息是,这并不难! - Lightness Races in Orbit
3
@TomalakGeret'kal: 这是不可能的,通常你无法确定哪些文件是文本文件,哪些是二进制文件。现在想象一下两台机器,一台是运行Linux系统的,另一台是运行Windows系统的,它们都在向同一个网络附加存储设备中的文件写入数据。那么应该由谁来负责文件的转换以及转换什么内容呢? - Yakov Galka
2
@ybungalobill:如果您是唯一一个写入文件的人,那就太好了。不幸的是,您必须应对其他程序编写的文件(这里肯定有FileZilla和可能会写入文件的其他文本编辑器(以与读取的内容不同的修改格式))。因此,您确实需要详细解释正在发生的事情,以便OP考虑到这些其他程序并适当地进行补偿。尽管我在小规模上同意您的技术,但对于OP将遇到的一般情况来说,这还不足够的信息。 - Martin York
@ybungalobill:如果原帖作者不知道他面前的文件格式,那么问题就非常严重了。 - Lightness Races in Orbit
@TomalakGeret'kal:你之前提到了“任意用户”和“文件传输”,但他们都不是OP。 - Yakov Galka

6

endl在Linux环境下可以“很好地工作”。使用流操作符endl会输出一个换行符\n并且刷新缓冲区。但是,在Windows中,文本模式下的文件流会在实现层面将这个\n转换成\r\n,当你在不同平台之间传输文件时,也会发现换行符被转换了。

这可能不是C++的问题,也没有什么“损坏”了;你应该将FileZilla配置为将你的文件视为“文本”,而不是“二进制”(在该模式中,换行符不会被转换)。如果你的文件没有像“.txt”这样的扩展名,则默认情况下可能不会执行此操作。


另外两个人建议我以二进制方式打开,而你建议我以文本方式打开。两种方法都不起作用,这只会增加混乱。 - natli
1
@natli,你没尝试完成我的第三个要点,所以不要说它行不通。实际上,这是你在共享存储上打开文件时唯一的选择。最终重要的是你使用什么查看器,而不是确切写了什么。 - Yakov Galka
2
@natli:没有什么奇怪的。微软记事本作为文本查看器是个笑话。它只能理解\r\n,无法处理大文件等等...那又怎样?不用就好了。而我的第三条并没有涉及二进制和ASCII。 - Yakov Galka
@johndodo:虽然可能,但并不是很好。问题在于C++标准确实更喜欢使用\n作为行尾。正如Tomalak所指出的那样,在endl的定义中可以看到这一点。 - Yakov Galka
当他在文件传输软件中正确进行编码转换时,他仍然可以使用记事本。所有这些都与C++无关。 - Lightness Races in Orbit
显示剩余4条评论

3

如果你将文件以ASCII方式传输,FTP可能会弄乱你的文件(也就是说,它会转换换行符)。尝试以BIN(二进制)方式传输。


1
恰恰相反,问题在于行结尾符没有被转换为他所期望的格式。 - Lightness Races in Orbit
取决于他想要什么。我总是尝试进行自己的换行转换 - 自动化通常只会增加混乱......编写文本,以二进制形式传输,读取文本。它将与编写时的格式相同。如果您仍然遇到换行符问题,您可以解决一个较小的问题。 - johndodo
1
实际上,ybungalobill已经总结得很好了。 :) - johndodo

2

所有应用程序内部都使用“\n”表示行终止符。

问题在于,行终止序列对于文本文件是特定于平台的(正如您的研究所发现的)。注意: 文本文件,这是打开文件时的默认格式。如果您明确选择二进制文件,则读取/写入时不会进行任何转换。

实际上,这意味着当您将'\n'字符写入文件时,它会转换为特定于平台的字符序列。但也请注意,读取文件时,该平台特定的序列会转换回'\n'。您遇到的问题是:您已在一个平台上编写文件,然后在另一个平台上读取这些文件。

在Linux上,行终止序列是LF('\n')。因此,您写入文件时,所有' \ n'都将转换为“ LF”字符。您将这些文件传输到Windows系统,现在读取文件。在Windows上,行终止序列是'CRLF',因此读取文件的编辑器正在寻找两个字符以将其转换回'\n',但却找不到这些字符。现在就取决于编辑器的智能程度,无论您是否获得单个行或多个行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接