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

Phi*_*han 265 files newlines

一些编译器(尤其是 C 或 C++ 编译器)会发出以下警告:

No new line at end of file
Run Code Online (Sandbox Code Playgroud)

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

\ No newline at end of file
Run Code Online (Sandbox Code Playgroud)

对于 PHP 文件。

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

在文件末尾换行有什么意义?

Gil*_*il' 307

这不是在文件末尾添加额外的换行符,而是不删除应该在那里的换行符。

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;这是在这些操作系统上读取作为文本打开的文件时经常发生的情况)。有一些操作系统没有换行符,而是有固定或可变大小的记录;在这些系统上,从文件到 C 源的映射引入了一个\n在每条记录的末尾。虽然这与 unix 没有直接关系,但它确实意味着如果您将缺少最终换行符的 C 源文件复制到具有基于记录的文本文件的系统中,然后将其复制回来,您最终会得到不完整的最后一行在初始转换中被截断,或者在反向转换期间附加了一个额外的换行符。

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

  • 为什么不直接更新古老的工具,这样如果缺少新行,它们就不会中断...... (2认同)

Ser*_*sta 67

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

考虑一下如果您想使用cat. 例如,如果您想foo在 3 个文件的行首找到单词:

cat file1 file2 file3 | grep -e '^foo'
Run Code Online (Sandbox Code Playgroud)

如果文件 3 中的第一行以 开头foo,但文件 2\n在其最后一行之后没有结尾,则 grep 将找不到这种情况,因为文件 2 中的最后一行和文件 3 中的第一行将被 grep 视为单个线。

所以,为了一致性和避免意外,我尽量让我的文件总是以新行结尾。

  • 这就像在说,“有时我将字符串附加在一起,末尾有 `\n` 或空格,所以为了保持一致,我总是在字符串的两端加上 `\n _____`。” 好吧,不,正确的做法是修剪字符串,然后正确连接它们。 (9认同)
  • @ViktorKruglikov 关心你的文件内容是 git 的事,是的。Git 可以帮助您保持数据的一致性。如果它忽略换行符,它就会失败。按照同样的思路,猫的职责就是准确地再现你给它的数据;它不得在您没有特别要求的情况下添加随机字符。 (4认同)
  • 你应该在 cat 操作中加入 `'\n'` 难道不合理吗? (3认同)
  • 但是关心文件连接是 git 的事情吗? (2认同)

max*_*zig 17

有两个方面:

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

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

  • @安德鲁,不,不是。`diff` 预计会打印差异(如果有)。如果一个文件的最后一个字符有换行符,而另一个文件没有,那么这种差异在输出中一定是明显的。 (3认同)

Sté*_*nez 11

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

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

注意:关于 C 编译器的警告,我猜他们坚持为了向后兼容目的而使用最后的换行符。如果不以\n(或其他依赖于系统的行尾字符序列结尾),非常旧的编译器可能不接受最后一行。

  • *“我猜他们为了向后兼容的目的坚持使用最后的换行符”* - 不,他们坚持这样做是因为 C 标准 *强制* 它。 (10认同)
  • @MestreLion:为什么你认为 C 标准强制这样做...... (2认同)

小智 7

还有一点是保留差异历史。如果文件结束时没有换行符,那么在文件末尾添加任何内容将被 diff 实用程序视为更改最后一行(因为\n正在添加到其中)。

这可能会导致诸如git blame和 之类的命令产生不需要的结果hg annotate

  • 不同的工具是智能的。他们注意到文件的细微变化(这很重要,因为它不可避免地会改变文件的哈希值)。GNU diff 和 git diff 都接受 `-w` 选项,以在为人类输出数据时忽略空格更改。 (2认同)

Che*_*dor 7

这是另一个原因。假设您有一个file.txt包含名称列表的文件,每行一个名称(或者考虑一个文件,例如 .gitignore 文件)。

要添加新条目,可以调用如下命令:

echo "John" >> file.txt
Run Code Online (Sandbox Code Playgroud)

你希望这总是有效吗?

好吧,实际上只有当您的文件以换行符结尾时它才会起作用(无论您的文件是否为空)。

否则,假设您的文件包含:

Alice<no_newline>
Run Code Online (Sandbox Code Playgroud)

当您发送您的:

echo "John" >> file.txt
Run Code Online (Sandbox Code Playgroud)

你最终会得到:

AliceJohn
<new_line_here>
Run Code Online (Sandbox Code Playgroud)

这绝对不是你所期望的。因此,所有文本文件都以 EOL 终止会使生活变得更加轻松。


小智 5

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

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

因此,为了将最后一行识别为实际的“行”,它应该有一个终止的新行字符。

如果您依赖操作系统工具说行数或拆分/帮助解析您的文件,这很重要。鉴于 PHP 是一种脚本语言,它完全有可能,尤其是在早期甚至现在(我不知道/假设)它具有这样的操作系统依赖性。

实际上,大多数操作系统并不完全符合 POSIX 标准,人类不像机器那样甚至不关心终止新线路。所以对于大多数事情来说,它是所有关心它的东西的大杂烩,警告或只是去最后一点文本真的是一行,所以只需包含它。