文件末尾没有换行符


471

执行此操作时git diff,显示“文件末尾没有换行符”

好的,文件末尾没有换行符。有什么大不了的?

该消息的意义是什么,它试图告诉我们什么?


11
也许,如果您的文件结尾没有换行符,并且添加了另一行,那么git必须显示前一行已经更改,因为它包括换行符作为该行的一部分?
nafg 2014年

Answers:


458

它表示'\n'文件末尾没有换行符(通常为CR或CRLF)。

也就是说,简单地说,文件中的最后一个字节(或字节,如果您使用的是Windows)不是换行符。

之所以显示该消息,是因为否则无法分辨出文件末尾有换行,而文件末尾没有换行。Diff无论如何都必须输出换行符,否则结果将更难于自动读取或处理。

请注意,如果文件格式允许,总是将换行符放在最后一个字符是一种很好的样式。此外,例如,对于C和C ++头文件,语言标准是必需的。


135
出于好奇,您能解释为什么总是将换行符作为最后一个字符被认为是一种好风格吗?编辑:找到了这个讨论
Paul Bellora 2012年

84
@PaulBellora历史上,这是由C语言标准stackoverflow.com/a/729725/233098做出的决定。实际上,因为许多Unix工具都需要或期望它来正确显示stackoverflow.com/a/729795/233098。从哲学上讲,由于文本文件中的每一行都以“行尾”字符结尾,因此最后一行不应例外。以不同的方式思考它,让我们探索逆向。如果有一个“行首”标记而不是“行尾”,您是否会在第一行省略“行首”字符?
2014年

29
@Joe没什么意义。换行符是换行符,即之间的分隔符,而不是行尾。我们没有换行符,因为它们不是必需的。出于相同的原因,我们没有行尾字符。
2014年

6
@acjay我认为“行之间的分隔符”与“行尾”之间本质上更好。两种观点本质上都不对还是错,只是一种看待它的方法。我建议我们继续使用点的观点,是历史上实际的,因为我们已经在做了这种方式,它的确,当你接受它是有意义的。一致性很重要。无需以“行之间的分隔符”的观点来打破这一点。
2014年

17
@WORMSS“对我来说新手”与“新约定”不一样。这就像发现任何其他类型的编程约定一样。你随它去吧。您可能会偏离,但您只是孤立自己。(或者在这种情况下,实际上是在破坏工具。)请考虑有多少其他人发现了一些Rails约定或PEP8,以及这些社区作为一个整体的一致性,因为它们确实给出了意见,尽管他们编写了代码,但是却相反。
2014年

100

这不仅是不良的样式,而且在文件上使用其他工具时,可能会导致意外的行为。

这里是test.txt

first line
second line

最后一行没有换行符。让我们看看文件中有多少行:

$ wc -l test.txt
1 test.txt

也许这就是您想要的,但是在大多数情况下,您可能希望文件中有2行。

另外,如果您要合并文件,则可能无法达到预期的效果:

$ cat test.txt test.txt
first line
second linefirst line
second line

最后,如果要添加新行,这会使您的差异变得更加嘈杂。如果添加了第三行,它将显示对第二行以及新添加的内容的编辑。


4
cat的结果还可以,但是wc参数“ -l,--lines”是错误的。即使是手册,也说“打印换行计数”而不是“打印换行计数”。
令人难以置信的

而且我什至无法在最近的util linux(util-linux 2.34)上重现此(wc和cat)。
wget

1
@wget我在util-linux 2.34上,它可以确认此答案描述的是当前行为。我的猜测是您的编辑器添加了“ \ n”字符。
stephanos

29

唯一的原因是,Unix历史上一直以所有以可读行结尾的人类可读文本文件为惯例。当时,这避免了在显示或加入文本文件时进行额外的处理,并且避免了将文本文件与包含其他类型数据(例如,人类无法读取的原始二进制数据)的文件区别对待。

由于这个约定,那个时代的许多工具都希望结尾的换行符,包括文本编辑器,差异工具和其他文本处理工具。Mac OS X是在BSD Unix上构建的,而Linux已开发为与Unix兼容的,因此两种操作系统都继承了相同的约定,行为和工具。

Windows尚未开发为与Unix兼容,因此它没有相同的约定,并且大多数Windows软件都可以很好地处理而没有尾随换行符。

但是,由于Git首先是为Linux开发的,并且许多开放源代码软件都是在与Unix兼容的系统上构建的,例如Linux,Mac OS X,FreeBSD等,因此大多数开放源代码社区及其工具(包括编程语言)仍在继续遵守这些约定。

有许多技术原因在1971年才有意义,但在这个时代,这主要是惯例并保持与现有工具的兼容性。


23

如果您在现有文件的末尾添加新的文本行,但末尾没有newline character,则diff将显示旧的最后一行已被修改,即使从概念上讲不是。

这至少是newline character在末尾添加a的一个很好的理由。

文件包含:

A() {
    // do something
}

十六进制转储:

00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20  A() {.    // do 
00000010: 736f 6d65 7468 696e 670a 7d              something.}

您现在将其编辑为

A() {
    // do something
}
// Useful comment

十六进制转储:

00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20  A() {.    // do 
00000010: 736f 6d65 7468 696e 670a 7d0a 2f2f 2055  something.}.// U
00000020: 7365 6675 6c20 636f 6d6d 656e 742e 0a    seful comment..

git diff将显示:

-}
\ No newline at end of file
+}
+// Useful comment.

换句话说,它显示出比概念上更大的差异。它显示您删除了该行}并添加了该行}\n。实际上,这是发生的事情,但从概念上讲不是发生的事情,因此可能会造成混淆。


2
我们可以从另一个方向写同样的东西:如果您在现有文件的末尾删除新行,而该新行已经在末尾添加了换行符,则diff将显示旧的最后一行也进行了修改,但从概念上讲不是。至少有一个很好的理由最后删除换行符。
gentiane

3
@gentiane您会混淆“换行”(换行)和“换行”(以1或2个字符分隔行尾)
minexew

@minexew不,金刚烷不是。也许您只是没有意识到“换行”与“换行”相同。
令人难以置信的

3
@TheincredibleJan在答案中使用它们的方式,两个术语具有不同的含义。我不知道您是想成为一个聪明人还是只是误解了正在发生的事情。
minexew

18

它仅表示文件末尾没有换行符。这不是灾难,它只是一个信息,它使您在命令行中查看差异时更加清楚地知道没有差异。


10

之所以采用这种约定,是因为在类似UNIX的操作系统上,换行符被视为行终止符和/或消息边界(这包括进程之间的管道,行缓冲等)。

例如,考虑将只有换行符的文件视为空行。相反,长度为零字节的文件实际上是具有零行的空文件。可以根据wc -l命令确认。

总之,此行为是合理的,因为如果\n字符只是行分隔符而不是行终止符,则没有其他方法可以区分空文本文件和具有单个空行的文本文件。因此,有效的文本文件应始终以换行符结尾。唯一的例外是如果文本文件打算为空(无行)。


1
为什么我拒绝投票-2?我不仅指出了其他答案的证实(即基于UNIX的标准工具期望换行符作为行的终止符),而且还没有办法将空文件与单个空行区分开,这是绝对正确的。我专门回答了原始问题“该消息的意义是什么,它试图告诉我们什么?”
Leslie Krause

我没有投票给您,但是此响应似乎特定于Unix类型的系统,因为它仅在换行符只是换行符时才适用。目前尚不清楚在这里是否适用。另外,如果文件仅包含一个空行,则警告似乎无用。但是,我避免使用Stackoverflow,因为人们经常不加解释地投票。
user34660 '19

9

我在以前的回复中没有看到一件事。当文件的一部分被截断时,没有行尾的警告可能是警告。这可能是数据丢失的症状。


总的来说,这是个好主意,但对于这个特定问题,我认为这没有道理。
cst1992 '19

@ cst1992 Stackoverflow中的答案应该尽可能有用,这意味着它们应该适用于所有可能性。问题很简短,我看不出它在哪里排除了我建议的可能性。
user34660 '19

7

核心问题是如何定义行以及行尾字符序列是否为行的一部分。基于UNIX的编辑器(例如VIM)或工具(例如Git)使用EOL字符序列作为行终止符,因此它是行的一部分。类似于在C和Pascal中使用分号(;)。在C中,分号终止语句,在Pascal中,将语句分开。



3

源文件通常由工具(C,C ++:头文件,Javascript:捆绑器)连接。如果省略换行符,则可能会引入讨厌的错误(其中一个源的最后一行与下一个源文件的第一行串联在一起)。希望所有的源代码concat工具都可以在级联文件之间插入换行符,但事实并非总是如此。

问题的症结在于-在大多数语言中,换行符具有语义含义,而文件结尾不是由语言定义的换行符替代方案。因此,您应该使用换行符终止每个语句/表达式,包括最后一个。


1
在C / C ++中,您可以用一行编写整个项目。无需换行。
令人难以置信的

可以写你的整个项目在一行...如果你不使用//代码的中间语言风格的注释。
道格·柯本

2

您的原始文件可能没有换行符。

但是,某些编辑器(如linux中的gedit)会在文件末尾静默添加换行符。使用这种编辑器时,您不会摆脱此消息。

我试图克服的问题是使用Visual Studio代码编辑器打开文件

该编辑器清楚地显示了最后一行,您可以根据需要删除该行。


0

对于它的价值,当我在Mac上创建IntelliJ项目,然后将该项目移到我的Windows计算机上时,遇到了这个问题。我必须手动打开每个文件并更改IntelliJ窗口右下角的编码设置。如果有任何阅读此问题的人,也许对大多数人来说都没有发生,但这可以为我节省几个小时的工作...

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.