协慌网

登录 贡献 社区

为什么文本文件以换行符结尾?

我假设这里的每个人都熟悉所有文本文件应以换行符结尾的格言。多年来我一直都知道这个 “规则”,但我一直在想 - 为什么?

答案

因为这是POSIX 标准定义一条线的方式

3.206 线
一系列零个或多个非 字符加上一个终止 字符。

因此,不以换行符结尾的行不被视为实际行。这就是为什么某些程序在处理文件的最后一行时遇到问题,如果它不是换行符。

在终端仿真器上工作时,本指南至少有一个硬性优势:所有 Unix 工具都希望使用此约定并使用它。例如,当使用cat连接文件时,由换行符终止的文件将具有与不具有以下内容的文件不同的效果:

<i>$</i> <b>more</b> a.txt
foo
<i>$</i> <b>more</b> b.txt
bar <i>$</i> <b>more</b> c.txt
baz
<i>$</i> <b>cat</b> {a,b,c}.txt
foo
barbaz

并且,正如前面的示例所示,当在命令行上显示文件时(例如,通过more ),换行符的换行文件会导致正确的显示。未正确终止的文件可能会出现乱码(第二行)。

为了保持一致性,遵循此规则非常有帮助 - 否则在处理默认的 Unix 工具时会产生额外的工作。


以不同的方式思考:如果换行没有终止行,那么使诸如cat命令变得更加困难:如何创建一个连接文件的命令,以便

  1. 它将每个文件的开头放在一个新行上,这是你想要的 95%的时间; 但
  2. 它允许合并两个文件的最后一行和第一行,如上面b.txtc.txt之间的例子b.txt

当然这是可以解决的,但你需要让cat的使用更复杂(通过添加位置命令行参数,例如cat a.txt --no-newline b.txt c.txt ),现在命令而不是每个人 file 控制它与其他文件粘贴的方式。这几乎肯定不方便。

... 或者你需要引入一个特殊的哨兵角色来标记一条应该继续而不是终止的线。好吧,现在你遇到了与 POSIX 相同的情况,除了反转(行继续而不是行终止字符)。


现在,在非 POSIX 兼容系统(现在主要是 Windows)上,重点是:文件通常不以换行符结束,而行的(非正式)定义可能是 “由换行符分隔的文本” (注意重点)。这完全有效。但是,对于结构化数据(例如编程代码),它使解析变得更加复杂:它通常意味着必须重写解析器。如果解析器最初是用 POSIX 定义编写的,那么修改令牌流而不是解析器可能更容易 - 换句话说,在输入的末尾添加 “人工换行” 令牌。

每一行都应以换行符结尾,包括最后一行。如果某个程序不是换行符,则会在处理文件的最后一行时遇到问题。

GCC 警告它不是因为它无法处理文件,而是因为它必须作为标准的一部分。

C 语言标准说一个非空的源文件应以换行符结尾,换行符前面不应该有反斜杠字符。

由于这是一个 “shall” 子句,我们必须发出违反此规则的诊断消息。

这在 ANSI C 1989 标准的 2.1.1.2 节中。 ISO C 1999 标准的 5.1.1.2 节(也可能是 ISO C 1990 标准)。

参考: GCC / GNU 邮件存档

这个答案是尝试技术答案而不是意见。

如果我们想成为 POSIX 纯粹主义者,我们将一行定义为:

一系列零个或多个非 字符加上一个终止 字符。

资料来源: http//pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206

一条不完整的行:

文件末尾的一个或多个非 字符的序列。

资料来源: http//pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195

文本文件为:

包含组织为零行或多行的字符的文件。这些行不包含 NUL 字符,长度不能超过 {LINE_MAX} 个字节,包括 字符。尽管 POSIX.1-2008 不区分文本文件和二进制文件(请参阅 ISO C 标准),但许多实用程序在操作文本文件时仅产生可预测或有意义的输出。具有此类限制的标准实用程序始终在其 STDIN 或 INPUT FILES 部分中指定 “文本文件”。

资料来源: http//pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397

字符串为:

由第一个空字节终止并包括第一个空字节的连续字节序列。

资料来源: http//pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396

从这以后,我们可以得出的唯一一次,我们将有可能遇到什么类型的问题是,如果我们处理一个的文件或文件为文本文件 (是一个文本文件是零的组织概念或更多行,我们知道的行必须以 结束。

例证: wc -l filename

wc的手册中我们读到:

行被定义为由 字符分隔的字符串。

对 JavaScript,HTML 和 CSS 文件有什么影响,那么它们是文本文件?

在浏览器,现代 IDE 和其他前端应用程序中,在 EOF 中跳过 EOL 没有问题。应用程序将正确解析文件。由于并非所有操作系统都符合 POSIX 标准,因此非 OS 工具(例如浏览器)根据 POSIX 标准(或任何操作系统级标准)处理文件是不切实际的。

因此,我们可以相对确信 EOF 的 EOL 在应用程序级别几乎没有负面影响 - 无论它是否在 UNIX OS 上运行。

在这一点上,我们可以自信地说,在客户端处理 JS,HTML,CSS 时,在 EOF 上跳过 EOL 是安全的。实际上,我们可以声明缩小其中任何一个文件,不包含 是安全的。

我们可以更进一步说,就 NodeJS 而言,它也不能遵守 POSIX 标准,因为它可以在非 POSIX 兼容环境中运行。

那我们还剩下什么?系统级工具。

这意味着可能出现的唯一问题是使用工具将其功能与 POSIX 的语义相结合(例如,如wc所示定义行)。

即便如此,并非所有 shell 都会自动粘贴到 POSIX 上。例如,Bash 不默认为 POSIX 行为。有一个开关启用它: POSIXLY_CORRECT

关于 EOL 价值的思考的食物是 http//www.rfc-editor.org/EOLstory.txt

保持工具轨道,出于所有实际意图和目的,让我们考虑一下:

让我们使用没有 EOL 的文件。在撰写本文时,此示例中的文件是一个没有 EOL 的缩小 JavaScript。

curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o x.js
curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o y.js

$ cat x.js y.js > z.js

-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 x.js
-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 y.js
-rw-r--r--  1 milanadamovsky  15810 Aug 14 23:18 z.js

请注意, cat文件大小正是其各个部分的总和。如果 JavaScript 文件的串联是 JS 文件的一个问题,那么更合适的问题是用分号启动每个 JavaScript 文件。

正如有人在此线程提到的还有:如果你想cat两个文件的输出变成只有一条线路,而不是两个?换句话说, cat会做它应该做的事情。

catman只提到阅读输入到 EOF,而不是 。请注意, cat-n开关也会打印出非 <换行> 终止的行(或不完整的行 )作为一行 - 计数从1开始(根据该man说法)。

-n 编号输出行,从 1 开始。

现在我们已经理解了 POSIX 如何定义一条线 ,这种行为变得模棱两可,或者真的不合规。

了解给定工具的目的和合规性将有助于确定使用 EOL 结束文件的重要性。在 C,C ++,Java(JAR)等中...... 一些标准将规定有效性的换行符 - JS,HTML,CSS 没有这样的标准。

例如,不使用wc -l filename ,而是可以执行awk '{x++}END{ print x}' filename ,并确保任务的成功不会受到我们可能想要处理的文件的危害(我们没有写入)例如第三方库,例如缩小的 JS,我们curl d) - 除非我们的意图是真正计算符合 POSIX 标准的

结论

现实生活中的用例非常少,在 EOF 中为某些文本文件(如 JS,HTML 和 CSS)跳过 EOL 会产生负面影响 - 如果有的话。如果我们依赖 存在,我们将工具的可靠性仅限于我们创作的文件,并将自己打开以防止第三方文件引入的潜在错误。

故事的道德:在 EOF 中没有依赖 EOL 的弱点的工程师工具。

随意发布用例,因为它们适用于 JS,HTML 和 CSS,我们可以检查跳过 EOL 如何产生负面影响。