“文件末尾没有换行符”日志的意义是啥?

Posted

技术标签:

【中文标题】“文件末尾没有换行符”日志的意义是啥?【英文标题】:What's the significance of the "No newline at end of file" log?“文件末尾没有换行符”日志的意义是什么? 【发布时间】:2011-08-14 08:58:14 【问题描述】:

在执行git diff 时,它会显示“文件末尾没有换行符”

这条信息的意义是什么,它试图告诉我们什么?

【问题讨论】:

也许,如果你有一个没有换行符结束的文件,并且你添加了另一行,git 必须显示前最后一行已更改,因为它包含换行符作为行吗? 【参考方案1】:

这表示文件末尾没有换行符(通常是'\n',又名 CR 或 CRLF)。

也就是说,简单地说,文件中的最后一个字节(或在 Windows 上的字节)不是换行符。

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

请注意,如果文件格式允许,始终将换行符放在最后一个字符是一种很好的风格。此外,例如,对于 C 和 C++ 头文件,它是语言标准所要求的。

【讨论】:

出于好奇,您能解释一下为什么总是将换行符作为最后一个字符被认为是一种很好的风格吗?编辑:找到this discussion。 @PaulBellora 从历史上看,这是由 C 语言标准 ***.com/a/729725/233098 做出的决定,实际上,因为许多 Unix 工具需要或期望它才能正确显示 ***.com/a/729795/233098。从哲学上讲,因为文本文件中的每一行都以“行尾”字符结尾——最后一行不应该有任何例外。换个角度想,让我们探索一下逆向。如果有一个“行首”标记而不是“行尾”,您会省略第一行的“行首”字符吗? @Joe 这没有多大意义。换行符是 new line,即行之间的分隔符,而不是行尾。我们没有行首字符,因为它们不是必需的。出于同样的原因,我们没有行尾字符。 @acjay 我认为“行间分隔符”与“行尾”之间本质上更好。这两种观点在本质上都没有对错之分,只是一种看待它的方式。我建议我们继续使用历史上实用的观点,因为我们已经这样做了,并且当你接受它时它确实有意义。一致性很重要。没有必要以“行间分隔符”观点的名义打破它。 @WORMSS “New to me”与“a new convention”不同。这就像发现任何其他类型的编程约定一样。你就随它去吧。你可以偏离,但你只是孤立自己。 (或者在这种情况下,实际上破坏了工具。)想想有多少其他人发现了一些 Rails 约定或 PEP8,以及这些社区作为一个整体保持的一致性,因为他们确实提供了——尽管编写了相反的代码。跨度> 【参考方案2】:

这不仅是糟糕的风格,它还可能导致在文件上使用其他工具时出现意外行为。

这里是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

最后,如果您要添加一个新行,它会使您的差异稍微嘈杂一些。如果您添加了第三行,它将显示对第二行的编辑以及新添加的内容。

【讨论】:

cat 的结果没问题,但是 wc 参数 "-l, --lines" 是错误的。即使它的手册说“打印换行数”而不是“打印行数”。 我什至无法用最近的 util linux (util-linux 2.34) 重现这个(wc 和 cat)。 @wget 我在 util-linux 2.34 上,它可以确认这个答案描述的是当前行为。我的猜测是您的编辑添加了“\n”字符。 @TheincredibleJan 每行都以'\n' 结尾。如果没有'\n',则不是一行。 -l --lines 参数是绝对正确的。【参考方案3】:

如果您在现有文件末尾添加 新的一行文本,而该文件末尾还没有 newline character,则差异将显示旧的最后一行已被修改,尽管从概念上来说它不是。

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

示例

一个文件包含:

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 行。事实上,这就是发生的事情,但不是概念上发生的事情,因此可能会令人困惑。

【讨论】:

我们可以在另一个方向上写同样的东西:如果你在现有文件的末尾删除一个新行,而该文件末尾已经有一个换行符,差异也会显示旧的最后一行经修改,但在概念上并非如此。至少有一个很好的理由在末尾删除换行符。 @gentiane 你混淆了“a new line”(新行)和“a newline”(1 或 2 个字符分隔行尾) @minexew 不,龙胆草不是。也许你只是没有意识到“a new line”和“a newline”是一样的。 @TheincredibleJan 它们在答案中的使用方式,这两个术语具有不同的含义。我不知道你是想成为一个聪明人还是只是误解了正在发生的事情。 @minexew 我不明白你,龙胆是对的。【参考方案4】:

唯一的原因是 Unix 历史上所有人类可读的文本文件都以换行符结尾的约定。当时,这避免了在显示或连接文本文件时进行额外处理,并避免将文本文件区别于包含其他类型数据的文件(例如人类不可读的原始二进制数据)。

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

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

但是,由于 Git 最初是为 Linux 开发的,而且很多开源软件都是建立在与 Unix 兼容的系统上,如 Linux、Mac OS X、FreeBSD 等,因此大多数开源社区及其工具(包括编程语言)继续遵循这些约定。

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

【讨论】:

您的回答暗示它只有历史原因。这是错误的。 1971 年适用的相同原因在今天仍然适用。其他答案提供了更多细节【参考方案5】:

它只是表示文件末尾没有换行符。这不是一场灾难,它只是一条信息,让您在查看命令行中的差异时更清楚地表明没有差异。

【讨论】:

【参考方案6】:

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

例如,考虑将仅包含换行符的文件视为单个空行。相反,长度为零字节的文件实际上是一个零行的空文件。这可以通过wc -l命令来确认。

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

【讨论】:

为什么我被否决-2?我指出不仅确认了其他答案所说的内容(即基于标准 UNIX 的工具期望换行符作为行的终止符),而且没有办法将空文件与单个空行区分开来,这是绝对正确的.我专门回答了最初的问题“消息的意义是什么,它试图告诉我们什么?” 我没有对您投反对票,但此响应似乎特定于 Unix 类型系统,因为它仅适用于换行符只是换行符的情况。不清楚这是否适用于此。此外,如果文件仅包含一个空行,则该警告似乎毫无用处。但是我避免使用 ***,因为人们经常在没有解释的情况下投反对票。【参考方案7】:

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

【讨论】:

一般来说好点,但我认为在这个特定问题的上下文中没有意义。 @cst1992 *** 中的答案应该尽可能有用,这意味着它们应该适用于所有可能性。这个问题很简短,我看不出它排除了我建议的可能性。【参考方案8】:

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

【讨论】:

【参考方案9】:

这实际上确实会导致问题,因为行尾会自动修改脏文件而不对其进行任何更改。请参阅此帖子以获取解决方案。

git replacing LF with CRLF

【讨论】:

【参考方案10】:

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

问题的症结在于 - 在大多数语言中,换行符具有语义含义,并且文件结尾不是换行符的语言定义替代方案。所以你应该用换行符结束每一个语句/表达式——包括最后一个。

【讨论】:

在 C/C++ 中,您可以在一行中编写整个项目。不需要换行符。 可以将整个项目写在一行中...如果您不在代码中间使用// 样式注释。【参考方案11】:

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

但是,一些编辑器,如 linux 中的 gedit,会在文件末尾默默地添加换行符。使用此类编辑器时,您无法摆脱此消息。

我试图克服这个问题是用visual studio code editor打开文件

此编辑器清楚地显示最后一行,您可以随意删除该行。

【讨论】:

【参考方案12】:

不管怎样,我在 Mac 上创建 IntelliJ 项目时遇到了这个问题,然后将项目移到我的 Windows 机器上。我不得不手动打开每个文件并更改 IntelliJ 窗口右下角的编码设置。大多数读过这个问题的人可能不会发生这种情况,但这可以为我节省几个小时的工作......

【讨论】:

【参考方案13】:
ubuntu$> vi source.cpp

:set binary noeol

【讨论】:

这没有提供问题的答案(即“消息的意义是什么,它试图告诉我们什么?”)。要批评或要求作者澄清,请在他们的帖子下方留下评论。 - From Review 另外,这个问题已有 9 年的历史了,并且除了您和您​​的答案之外,还有近 +500 票和 14 个其他答案的公认答案。你的话题如何增加话题?

以上是关于“文件末尾没有换行符”日志的意义是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Python flake8 py错误地报告W391(文件末尾没有换行符)

为啥我必须费心在每个文件的末尾添加换行符?

错误:文件末尾没有换行符 [-Werror,-Wnewline-eof]#endif 在我的 IOS 游戏上进行适当加载时

具有 TaskStart 行但没有 TaskEnd 行的配置单元日志的意义是啥? [关闭]

Store在Redux中的意义是啥?

\\s正则表达式是啥意思 即String regex="\\.\\s*";是啥意思