为什么GitHub要求文件的末尾必须有换行符?

标签:#Linux##Unix##Windows##换行符##编程# 时间:2022/03/06 17:52:21 作者:小木

这几天逛reddit的时候发现了一个很有意思的讨论,有个童鞋说他在GitHub上提交代码的时候发现了提交文件被提示有一个红色警告的提示,鼠标移动上去会告诉你“No newline at end of file”(也就是文件末尾没有换行)。因此,他很奇怪,他不懂为什么GitHub要求文件的末尾必须有换行符。这个问题引起了很多的讨论。这里我也顺便记录共享一下。


这个问题在很早之前(大约12年前的2010年在StackOverflow上的问题)提问过了。文件末尾需要时空白行或者说最后一行内容必须以换行符结尾并不是一种强制性的要求,在GitHub里面也不算是一种错误,只是一种比较好的习惯。

在POSIX的规定中,对于文件的行(line)的定义如下:

3.206 Line
A sequence of zero or more non-newline characters plus a terminating newline character.

也就是说一个由零个或者多个非换行符(non-newline)字符加一个结束性的换行字符组成的序列。意思就是只要有一个换行符标志,那么就换行符前面的(非换行符)内容都是一行了。有点拗口,其实就是规定文件中的一行必须以换行符结尾,就是这个意思。

那么,没有换行符的行在很多程序中都不被认为是真正的行,进而可能会导致出现问题。那么,如果一个文件的末尾没有换行符,那么有些程序会觉得这个文件没有结束,还有新的内容,但是也读取不到,因此会报错。

这个规定基本上被所有Unix类的工具所遵循。例如如下命令:

$ more a.txt
foo
$ more b.txt
bar$ more c.txt
baz
$ cat {a,b,c}.txt
foo
barbaz

显然,文件末尾有换行符的文件,在执行more或者cat命令的时候会在终端显示上完全正确,反之则会出现莫名其妙的问题。因此,遵循这样的规范对于使用默认的Unix工具来说也是非常有价值的。

但是POSIX的标准主要针对的是Unix工具,他们认为一行结束的标志是\n,而Windows的规范则认为一行的结束标志是\r\n(也就是说回车符+换行符)。那么我们继续看如下的例子:

one\n
two\n
three

这是某个文件的内容。一般来说我们都认为这是一个三行文件。但是POSIX标准中,这是一个残缺的文件,因为文件末尾缺少换行符。那么,对于第二行我们怎么看呢?

对于Windows来说,第二行是two,因为Windows认为文件是由换行符隔开的行组成
而对于POSIX标准来说,第二行是two\n

区别就在于Windows认为结束标志不属于行内容,而POSIX认为是。

这种差别会导致什么样的结果呢,我们继续看下面的两种情况,第一种是我们把最后一行内容多复制几次,得到如下文件

one\n
two\n
threethreethreethree

显然,这不是我们希望得到的结果,理论上多复制最后一行应该得到更多行的内容。

第二种情况是我们交换第三行和第二行:

one\n
threetwo\n

看到没,三行文件变成两行了,这就是一行内容如果不带换行符出现的问题。显然,如果大家都遵循POSIX标准,这样在追加文件,复制文件中的行甚至是交互行都不会有问题。

尤其是追加内容,如果此前文件末尾不是换行符结尾,那么真的很容易出错。

本篇博客的主要内容是说明文件中行的概念和规范。虽然是一个不怎么被关注的内容,但却是很重要的。

欢迎大家关注DataLearner官方微信,接受最新的AI技术推送