在文件末尾添加新行有什么意义?


165

一些编译器(尤其是C或C ++编译器)会向您发出有关以下内容的警告:

No new line at end of file

我以为这只是C程序员的问题,但是github在commit视图中显示了一条消息:

\ No newline at end of file

一个PHP文件。

我了解此线程中解释的预处理器内容,但是与PHP有什么关系?是同一include()件事还是与\r\nvs \n主题有关?

在文件末尾添加新行有什么意义?



2
惹恼了人们。
安德鲁

3
如果cat是文件,则下一个提示将以“。”结尾,如果该行不以换行符结尾。
亚伦弗兰克

Answers:


185

这不是在文件末尾添加额外的换行符,而是关于不删除应存在的换行符。

Unix下的文本文件由一系列行组成,每换行符\n结尾。因此,不为空且不以换行符结尾的文件不是文本文件。

本应在文本文件上运行的实用程序可能无法很好地应对不以换行符结尾的文件。例如,历史上的Unix实用程序可能会忽略最后一个换行符之后的文本。GNU实用程序具有对非文本文件进行适当处理的策略,大多数其他现代实用程序也是如此,但是对于缺少最后一个换行符的文件,您仍然可能会遇到奇怪的行为。

使用GNU diff,如果要比较的文件之一以换行符结尾而不是换行符结尾,则请注意该事实。由于diff是面向行的,因此无法通过为其中一个文件存储换行符(而不为其他文件存储换行符)来表明这一点-换行符对于指示diff文件中每一行开始和结束位置是必需的。因此,diff使用此特殊文本\ No newline at end of file将未以换行结尾的文件与已换行的文件区分开。

顺便说一下,在C上下文中,源文件类似地由一系列行组成。更准确地说,一个翻译单元是在观看实现定义为一系列线,其每一个必须以换行字符(结束的n1256 §5.1.1.1)。在UNIX系统上,映射很简单。在DOS和Windows上,每个CR LF序列(\r\n)都映射到换行符(\n;这是在这些OS上读取以文本形式打开的文件时总是发生的情况。有些操作系统没有换行符,而是有固定大小或可变大小的记录。在这些系统上,从文件到C源代码的映射引入了\n在每条记录的末尾。虽然这与unix并不直接相关,但这确实意味着,如果将缺少最终换行符的C源文件复制到具有基于记录的文本文件的系统中,然后再将其复制回,则结果要么不完整最后一行在初始转换中被截断,或者在反向转换期间附加了一个新行。

¹ 示例:GNU sort的输出始终以换行符结尾。因此,如果文件foo缺少最后一个换行符,则会发现sort foo | wc -c报告的字符比少一个cat foo | wc -c


关于“ ...一系列行,每个行都必须以换行符结尾(n1256§5.1.1.1)”->在重新查看更新的C11dr N1570时,除以下以外,没有找到对此的支持: “不为空的源文件应以换行符结尾,在进行任何此类拼接之前,不得紧接在反斜杠字符之后。” §5.1.1.22,但这似乎仅限于拼接规范。
chux

@chux这句话也出现在n1256中。最后一行必须以换行符结尾。显然,不是最后一行的行也必须以换行符结尾,以指示该行结束并且下一行开始。因此,每行必须以换行符结尾。
吉尔斯

嗯,对我来说,“源文件...发生拼接。”这一行可能仅限于拼接注意事项,而不是一般文件。但是我看到别人可能会以其他方式查看。也许我会寻找一个帖子侧重于这一点。
chux

>“因此diff使用此特殊文本\在文件末尾没有换行符来区分未以换行符结尾的文件与已换行的文件。” Git不仅在比较文件时显示此文本。但是,即使将新文件添加到git中。我想这个论点是无效的。
维克多·克鲁格里科夫

>“本应在文本文件上运行的实用程序可能无法很好地应对未以换行符结尾的文件”,我认为考虑像POSIX这样的\ n缺失之类的低级问题并不是git的事要求。我认为,如果git显示此消息,则原因应该是源代码控制问题。
维克多·克鲁格里科夫

41

不一定是原因,而是文件未以换行结尾的实际后果:

考虑如果要使用处理多个文件,将会发生什么情况cat。例如,如果您想foo在3个文件的行首找到单词:

cat file1 file2 file3 | grep -e '^foo'

如果file3中的第一行以开头foo,但是file2 \n在其最后一行之后没有final ,则grep不会找到这种情况,因为grep会将file2中的最后一行和file3中的第一行视为单个线。

因此,为了保持一致性并避免出现意外,我尝试使文件始终以新行结尾。


但是,关心文件连接是git的事吗?
维克多·克鲁格里科夫

难道没有理由将您放在'\n'猫操作中吗?
安德鲁

3
这就像在说:“有时我将字符串的末尾附加\n空白,所以为了保持一致,我总是将\n _____字符串的两端都放在一起。” 好吧,不,正确的做法是修剪字符串,然后正确地将它们连接起来。
安德鲁

16

有两个方面:

  1. 有些/有些C编译器如果不以换行符结尾则无法解析最后一行。C标准指定C文件应以换行符结尾(C11、5.1.1.2、2),而没有换行符的最后一行将产生未定义的行为(C11,J.2,第二项)。也许出于历史原因,因为在编写第一个标准时,此类编译器的某些供应商才是委员会的一部分。因此由海湾合作委员会发出警告。

  2. diff程序(如git diff,github等使用)显示文件之间的逐行差异。当只有一个文件以换行符结尾时,它们通常会打印一条消息,因为否则您将看不到这种差异。例如,如果两个文件之间的唯一区别是最后一个换行符的存在,没有提示,则看起来两个文件都是相同的,当diffcmp返回退出代码不相等成功和文件的校验和时(例如,通过md5sum)不匹配。


差异程序有意义
Thamaraiselvam

听起来差异应该更聪明。
安德鲁

@安德鲁,不,不是。diff如果有的话,预计会打印出差异。而且,如果一个文件的最后一个字符使用换行符,而另一个文件则没有,那么输出中的区别一定很明显。
maxschlepzig

您后面的陈述是正确的。但是,差异查看器不必一开始就显示“换行符”(\n),而是可以简单地显示“换行符”。
安德鲁

10

\ No newline at end of file你得到github上出现在补丁的端部(diff格式见注在“统一格式”部分的结尾)。

编译器不在乎文件末尾是否有换行符,但是git(和diff/ patch实用程序)必须考虑到这些换行符。有很多原因。例如,忘记在文件末尾添加或删除换行符将更改其哈希值(md5sum/ sha1sum)。此外,文件并不总是程序,最终文件\n可能会有所不同。

注意:关于C编译器的警告,我想他们出于最终向后兼容的目的坚持使用最后的换行符。如果不以\n(或其他与系统相关的行末字符序列)结尾,很老的编译器可能不接受最后一行。


7
“我猜他们为实现向后兼容目的而坚持使用最后的换行符” -不,他们坚持这样做,因为C标准要求这样做。
MestreLion

1
@MestreLion C需要最后一个换行符以获取C源代码(C11§5.1.1.22)。请注意,对于文本文件I / O,C具有“是否在实现的最后一行定义了换行符”。§7.21.22
chux

谁在使用非常老的编译器?停止使用它们。
安德鲁

1
@MestreLion:为什么你认为C标准的任务吧...
斯特凡希门尼斯

@StéphaneGimenez:不同操作系统之间的一致性,更好的兼容性和互操作性(POSIX还定义了以'\ n'结尾的行)
MestreLion

4

POSIX,这是IEEE指定的一组标准,用于维护操作系统之间的兼容性。

其中之一是“行”的定义,该行是零个或多个非字符加上终止换行符的序列。

因此,要使最后一行被识别为实际的“行”,它应该具有终止的新行字符。

如果您依靠OS工具来说行数或分割/帮助解析文件,那么这很重要。鉴于PHP是一种脚本语言,它完全有可能,尤其是在早期甚至现在(我不知道/假定),它都具有类似OS的依赖关系。

实际上,大多数操作系统都不完全符合POSIX,人类也不是那种喜欢甚至不愿终止新生产线的机器。因此,对于大多数事情来说,它只是关心,警告或只是走最后一点文本的杂物,实际上是一行,因此只需将其包括在内。


3

保留差异历史记录也很重要。如果文件结尾没有换行符,则将所有内容添加到文件末尾将被diff实用程序视为更改最后一行(因为\n正在向其添加)。

使用诸如git blame和这样的命令可能会导致不必要的结果hg annotate


听起来差异仅需更智能。
安德鲁
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.