执行时git diff
会显示“文件末尾没有换行符”。
这条信息的意义是什么,它试图告诉我们什么?
'\n'
它表示文件末尾没有换行符(通常是CR 或 CRLF)。
也就是说,简单地说,文件中的最后一个字节(或者如果您在 Windows 上,则为字节)不是换行符。
显示该消息是因为否则无法区分文件末尾有换行符的文件和没有换行符的文件。无论如何,Diff 都必须输出换行符,否则结果将难以自动读取或处理。
请注意,如果文件格式允许,始终将换行符放在最后一个字符是一种很好的风格。此外,例如,对于 C 和 C++ 头文件,它是语言标准所要求的。
这不仅仅是糟糕的风格,在文件上使用其他工具时,它可能会导致意外行为。
这里是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
最后,如果您要添加新行,它会使您的差异更加嘈杂。如果您添加了第三行,它将显示对第二行的编辑以及新添加的内容。
如果您在现有文件的末尾添加新的文本行,而该文件末尾还没有 a newline character
,则差异将显示旧的最后一行已被修改,即使从概念上讲它不是。
这至少是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
。事实上,这就是发生的事情,但不是概念上发生的事情,因此可能会令人困惑。
唯一的原因是 Unix 历史上所有人类可读的文本文件都以换行符结尾的约定。当时,这避免了在显示或连接文本文件时进行额外处理,并避免将文本文件与包含其他类型数据的文件区别对待(例如人类不可读的原始二进制数据)。
由于这种约定,那个时代的许多工具都期望结束换行符,包括文本编辑器、差异工具和其他文本处理工具。Mac OS X 建立在 BSD Unix 之上,而 Linux 被开发为与 Unix 兼容,因此两个操作系统都继承了相同的约定、行为和工具。
Windows 不是为与 Unix 兼容而开发的,因此它没有相同的约定,并且大多数 Windows 软件都可以很好地处理没有尾随换行符。
但是,由于 Git 最初是为 Linux 开发的,而且很多开源软件都是建立在 Linux、Mac OS X、FreeBSD 等 Unix 兼容系统上的,因此大多数开源社区及其工具(包括编程语言)仍在继续遵守这些约定。
在 1971 年有一些技术原因是有意义的,但在这个时代,它主要是约定和保持与现有工具的兼容性。
它只是表明文件末尾没有换行符。这不是一场灾难,它只是一条信息,让您在查看命令行中的差异时更清楚地表明没有。
这种约定付诸实践的原因是因为在类 UNIX 操作系统上,换行符被视为行终止符和/或消息边界(这包括进程之间的管道、行缓冲等)。
例如,考虑将仅包含换行符的文件视为单个空行。相反,长度为零字节的文件实际上是一个零行的空文件。这可以根据wc -l
命令来确认。
\n
总而言之,这种行为是合理的,因为如果字符仅仅是行分隔符而不是行终止符,则没有其他方法可以区分空文本文件与具有单个空行的文本文件。因此,有效的文本文件应始终以换行符结尾。唯一的例外是文本文件是否为空(无行)。
我在之前的回复中没有看到一件事。当文件的一部分被截断时,关于没有行尾的警告可能是警告。这可能是缺少数据的症状。
核心问题是您定义的行以及行尾字符序列是否是行的一部分。基于 UNIX 的编辑器(如 VIM)或工具(如 Git)使用 EOL 字符序列作为行终止符,因此它是行的一部分。这类似于在 C 和 Pascal 中使用分号 (;)。在 C 中,分号终止语句,在 Pascal 中,它分隔它们。
这实际上确实会导致问题,因为行结尾会自动修改脏文件而不对其进行任何更改。请参阅此帖子以获取解决方案。
源文件通常由工具连接(C、C++:头文件,Javascript:捆绑器)。如果您省略换行符,您可能会引入讨厌的错误(其中一个源的最后一行与下一个源文件的第一行连接)。希望所有源代码 concat 工具无论如何都会在连接的文件之间插入换行符,但似乎并非总是如此。
问题的症结在于 - 在大多数语言中,换行符具有语义含义,并且文件结尾不是换行符的语言定义替代方案。所以你应该用换行符来结束每一个语句/表达式——包括最后一个。
您的原始文件可能没有换行符。
但是,一些编辑器(如linux 中的gedit)会在文件末尾默默地添加换行符。使用此类编辑器时,您无法摆脱此消息。
我试图克服这个问题是用Visual Studio 代码编辑器打开文件
这个编辑器清楚地显示了最后一行,您可以根据需要删除该行。
值得一提的是,我在 Mac 上创建 IntelliJ 项目时遇到了这个问题,然后将项目移到了我的 Windows 机器上。我不得不手动打开每个文件并更改 IntelliJ 窗口右下角的编码设置。大多数读过这个问题的人可能不会发生这种情况,但这可以为我节省几个小时的工作......
ubuntu$> vi source.cpp
:set binary noeol