换行
换行(英语:newline、line ending、end-of-line (EOL)、line Feed (LF)、line break),在计算机领域中是加在一行文字最后位置的特殊字符,在换行字符的下一个字符将会出现在下一行。实际上换行字符根据不同的硬件平台或操作系统平台会有不同的编码方式。
换行字符可以看作是行的结束符,也可以看作行之间的分隔符,这两种处理方式之间存在一些歧义。如果换行字符被当作分隔符,那么文件的最后一行就不需要再有换行字符。但是多数系统的做法是在最后一行的后面也加上一个换行字符,也就是把换行字符看作是行的结束符。这样的程序在处理末行没有换行字符的文件时,可能会存在问题。相反地,有的程序把换行符看作分隔符,就会把最末尾的换行字符看作是新行的开始,也就是多出了一个空行。
历史
早期计算机系统常使用电传打字机作为控制台设备,需要使用CR+LF字符序列将打印机定位在新行的开头。打印头无法及时从最右边返回到下一行的开头来打印下一个字符。在CR之后打印的字符通常会在页面中间打印为污迹,这是因为打印头仍在移回行首位置的途中。“解决方案是将换行符设置为两个字符:CR将打印头回车移动到第一列,LF将纸张向上移动。”[1]事实上,通常需要发送额外的但会被忽略的字符(无关的CR或 NUL)以给打印头更多时间移动到左边距。
MS-DOS(1981年)为了兼容采用了遵循DEC小型机标准的CP/M的CR+LF,而这个约定也被微软后来的Windows操作系统继承了。
1964年开始开发的Multics操作系统使用单独的LF作为换行符。Multics使用设备驱动程序将LF字符转换为打印机所需的任何字符序列(包括额外的填充字符),且单字节更便于编程。CR字符提供了将一行与另一行叠印以创建粗体、下划线和删除线效果的有用功能。单独使用LF作为行终止符已经被纳入最终的ISO/IEC 646标准草案中。Unix遵循了Multics的做法,后来类Unix系统也遵循了Unix。 这在Windows和类Unix操作系统之间造成了冲突,在一个操作系统上编写的文件无法正确格式化或由另一个操作系统解释。
表示
编程语言
printf("Hello world!\n");
Unicode
Unicode标准指定以下的字符为兼容标准的应用程序应识别的换行字符:[2]
- LF: 换行,U+000A
- VT: 垂直定位,U+000B
- FF: 换页符,U+000C
- CR: 回车符,U+000D
- CR+LF:CR(U+000D)后跟LF(U+000A)
- NEL: 下一行,U+0085
- LS: 分行,U+2028
- PS: 分段,U+2029
编程语言支持
为了便于创建可移植程序,编程语言提供了一些抽象来处理不同环境中使用的不同类型的换行序列。
C语言提供转义序列“\n”(换行符)和“\r”(回车符)。但是,这些字符不需要等同于ASCII编码中的LF和CR控制字符。C语言标准只保证两件事:
- 这些转义序列中的每一个都映射到一个唯一的实现定义的数字,该数字可以作为单个字符值存储。
- 当以文本模式写入文件、设备节点或socket/fifo时,“\n”会透明地转换为系统使用的本机换行序列,该序列可能比一个字符长。在文本模式下读取时,本机换行符序列将转换回 '\n'。在二进制模式下,不进行任何翻译,直接输出'\n'产生的内部表示。
在C起源的Unix平台上,本机换行序列是ASCII LF (0x0A),因此 '\n' 被简单地定义为该值。由于内部和外部表示相同,因此在文本模式下执行的翻译是无操作的,并且Unix没有文本模式或二进制模式的概念。这导致许多在Unix系统上开发软件的程序员完全忽略了这种区别,导致代码无法移植到不同的平台。
最好避免在二进制模式下使用C标准库函数fgets(),因为任何未使用Unix换行符约定写入的文件都将被误读。 另外,在文本模式下,任何未使用系统本机换行序列写入的文件(例如在 Unix 系统上创建的文件,然后复制到 Windows 系统)也会被误读。
另一个常见问题是在使用强制使用ASCII CR+LF 来结束行的Internet协议进行通信时使用“\n”。将 '\n' 写入文本模式流在Windows系统上可以正常工作,但在Unix上仅产生LF,而在更奇特的系统上则完全不同。在二进制模式下使用“\r\n”稍好一些。
许多语言,例如C++、Perl、Haskell都提供与C语言相同的 '\n' 解释。C++有另一种I/O模型,其中操纵器std::endl可用于输出换行符(并刷新流缓冲区)。
Java、PHP和Python提供 '\r\n' 序列(对应于ASCII的CR+LF)。与C不同,它们保证分别表示值U+000D和U+000A。
Java I/O库不会透明地将这些转换为输入或输出上依赖于平台的换行序列。 相反,它们提供了用于写入自动添加本机换行序列的整行的函数,以及用于读取接受CR、LF或CR+LF作为行终止符的行的函数。System.lineSeparator()方法可用于检索底层行分隔符。
C#例子:
string eol = Environment.NewLine;
string lineColor = "Color: Red" + eol;
string eol2 = "\n";
string lineColor2 = "Color: Blue" + eol2;
不同换行符格式带来的问题
不同的换行约定会导致在不同类型的系统之间传输的文本文件显示不正确。
使用类Unix或经典Mac OS上常见的程序创建的文件中的文本,在MS-DOS和Microsoft Windows常见的大多数程序中显示为单长行,因为这些程序不把单个换行符或单个回车符显示为换行。
相反,在类Unix系统上查看源自Windows计算机的文件时,额外的回车符可能会显示为第二个换行符、^M或每行末尾的<cr>。
此外,文本编辑器以外的程序可能不接受文件,例如一些配置文件,使用外部换行约定编码视作有效文件。
这个问题可能很难发现,因为有些程序可以正确处理外部换行符,而另一些则不能。例如,即使源文件在控制台或编辑器中显示时看起来正确,编译器也可能会因模糊语法错误而失败。现代文本编辑器通常可以识别所有类型的CR+LF换行符,并允许用户在不同标准之间进行转换。Web浏览器通常还能够显示使用不同类型换行符的文本文件和网站。
即使程序支持不同的换行符约定,这些功能通常也没有得到充分的标记、描述或记录。通常,将向用户显示枚举不同换行符约定的菜单或组合框,而不指示该选择是否将重新解释、临时转换或永久转换换行符。有些程序会在打开、复制、粘贴或保存时隐式进行转换。
大多数文本Internet协议包括 HTTP、SMTP、FTP、IRC 等)都要求在协议级别使用ASCII的CR+LF('\r\n', 0x0D 0x0A),但建议宽容的应用程序识别单独的LF ('\n', 0x0A) 也视作换行。尽管有规定的标准,许多应用程序错误地使用C语言换行转义序列 '\n' (LF),而不是回车转义序列和换行转义序列 '\r\n' (CR+LF) 的正确组合。 当尝试与遵循更严格的标准解释而不是建议的宽容解释的系统进行通信时,这种意外使用错误的转义序列会导致问题。qmail邮件传输代理就是这样一种不宽容的系统,它主动拒绝接受来自发送裸LF而不是所需CR+LF的系统的消息。[3]
电子邮件的标准互联网消息格式[4]规定:“CR和LF必须仅作为CRLF一起出现;它们不得独立出现在正文中”。
当传输以“ASCII模式”完成时,文件传输协议可以自动转换在具有不同换行符表示的系统之间传输的文件中的换行符。然而,以这种模式传输二进制文件通常会产生灾难性的结果:任何出现的换行符字节序列(在此上下文中没有行终止符语义,但只是正常字节序列的一部分)都将被转换为任何换行符表示形式其他系统使用,实质上损坏了文件。FTP客户端通常采用一些启发式方法(例如,检查文件扩展名)来自动选择二进制或 ASCII 模式,但最终由用户来确保其文件以正确的模式传输。如果对正确模式有任何疑问,应使用二进制模式,因为这样FTP不会更改任何文件,尽管它们可能显示不正确。[5]
相关条目
参考资料
- ^ Qualline, Steve. Vi Improved - Vim (PDF). Sams Publishing. 2001: 120 [4 January 2023]. ISBN 9780735710016. (原始内容 (PDF)存档于8 April 2022).
- ^ Unicode Standard Annex #14 UNICODE LINE BREAKING ALGORITHM. [2014-05-01]. (原始内容存档于2021-03-08).
- ^ Bernstein, D.J. Bare LFs in SMTP. [2023-08-19]. (原始内容存档于2011-10-29).
- ^ Resnick, Pete. Internet Message Format. April 2001 [2023-08-19]. RFC 2822(原出处存档于2016-06-13).
- ^ File Transfer. (原始内容存档于2016-05-14).
When in doubt, transfer in binary mode.