为什么文本文件以换行符结束?
我假设这里的每个人都熟悉所有文本文件都应该以换行符结尾的格言。 我已经知道了这个“规则”多年了,但是我一直在想 – 为什么?
因为这就是POSIX标准如何定义一行 :
- 3.206线
- 零个或多个非<新行>字符和一个终止的<新行>字符的序列。
因此,不以换行符结尾的行不被视为实际行。 这就是为什么有些程序在处理文件的最后一行时遇到问题,如果它不是换行符终止。
在terminal仿真器上工作时,这个指南至less有一个很大的好处:所有的Unix工具都希望这个约定能够使用它。 例如,当与cat
连接文件时,由换行符终止的文件将具有不同于不具有以下特征的文件:
$ more a.txt foo$ more b.txt bar $ more c.txt baz $ cat *.txt foobar baz
而且,如前面的例子所示,当在命令行上显示文件时(例如,通过more
),以换行符结尾的文件会导致正确的显示。 不正确地终止的文件可能会出现乱码(第二行)。
为了保持一致性,遵循这个规则是非常有用的 – 否则在处理默认的Unix工具时会招致额外的工作。
每行应以换行符结尾,包括最后一行。 某些程序在处理文件的最后一行时遇到问题,如果它不是换行符。
GCC警告说,这不是因为它不能处理文件,而是因为它必须作为标准的一部分。
C语言标准规定,非空的源文件应以换行符结束,不能立即以反斜杠字符开头。
由于这是一个“应该”条款,我们必须发出违反这条规则的诊断信息。
这是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}个字节,包括<newline>字符。 虽然POSIX.1-2008没有区分文本文件和二进制文件(参见ISO C标准),但是在文本文件上操作时,许多实用程序只能产生可预测或有意义的输出。 具有这种限制的标准实用程序始终在STDIN或INPUT FILES部分中指定“文本文件”。
来源: http : //pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397
一个string如下:
包含第一个空字节的连续字节序列。
来源: http : //pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396
从这里,我们可以推导出,我们唯一可能遇到的任何types的问题是,如果我们将文件行或文件的概念作为文本文件来处理 (即文本文件是一个零或更多的行,我们知道的一行必须以<newline>结尾)。
例如: wc -l filename
。
从wc
手册中我们看到:
一行被定义为一个由<换行符>字符分隔的string。
JavaScript,HTML和CSS文件对于文本文件有什么影响?
在浏览器,现代IDE和其他前端应用程序中,在EOF中跳过EOL没有任何问题。 应用程序将正确parsing文件。 因为并不是所有的操作系统都符合POSIX标准,所以对于非操作系统工具(如浏览器)来说,根据POSIX标准(或任何操作系统级标准)来处理文件是不切实际的。
因此,我们可以相对确信EOF在EOF上在应用程序级别上几乎没有负面影响 – 无论它是否在UNIX操作系统上运行。
在这一点上,我们可以自信地说,在客户端处理JS,HTML,CSS时,在EOF上跳过EOL是安全的。 实际上,我们可以说,缩小任何一个不包含<newline>的文件都是安全的。
我们可以进一步说,就NodeJS而言,它也不能坚持POSIX标准,因为它可以在非POSIX兼容环境中运行。
那我们剩下的是什么? 系统级工具。
这意味着唯一可能出现的问题是使用工具来努力使其function符合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
做它应该做的。
cat
的man
只提到阅读input到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
,并确信任务的成功不会受到我们可能想要处理的文件的危害,例如第三方库,比如我们curl
的缩小的JS) – 除非我们的意图是在POSIX合规意义上真正地计数行 。
结论
对于某些文本文件(例如JS,HTML和CSS),在EOF中跳过EOL的实际使用情况将非常less,如果有的话。 如果我们依靠<newline>存在,我们将工具的可靠性仅限制在我们编写的文件中,并且将自己置于由第三方文件引入的潜在错误之中。
道德故事:没有依靠EOF的弱点的工程师工具。
随意发布使用案例,因为它们适用于JS,HTML和CSS,我们可以检查如何跳过EOL有不利影响。
这可能与以下两者之间的差异有关:
- 文本文件(每行应该在行尾结束)
- 二进制文件(没有真正的“行”说,文件的长度必须保留)
如果每一行都在行结束时结束,例如,这可以避免连接两个文本文件会使第一次运行的最后一行进入第二行的第一行。
此外,编辑器可以在加载时检查文件是否在行尾结束,将其保存在本地选项“eol”中,并在写入文件时使用该选项。
几年前(2005年),许多编辑(ZDE,Eclipse,Scite,…)都忘记了最终的EOL, 这并不是很受赞赏 。
不仅如此,他们错误地将最终的EOL解释为“开始新的一行”,并且实际上开始显示另一行,就像它已经存在一样。
这是非常明显的一个“适当的”文本文件与像vim一样行事的文本编辑器,相比之下,在上述编辑器之一打开它。 它在文件的最后一行下面显示了一个额外的行。 你看到这样的东西:
1 first line 2 middle line 3 last line 4
有些工具期望这一点。 例如, wc
期望:
$ echo -n "Line not ending in a new line" | wc -l 0 $ echo "Line ending with a new line" | wc -l 1
基本上有很多程序不会正确处理文件,如果他们没有得到最终的EOL EOF。
GCC警告你,因为这是C标准的一部分。 (显然,第5.1.1.2节)
“文件末尾没有换行符”编译器警告
这起源于使用简单terminal的早期阶段。 换行符被用来触发传输数据的“刷新”。
今天,换行符不再是必需的了。 当然,如果换行符不存在,许多应用程序仍然有问题,但我会考虑这些应用程序中的错误。
但是如果你有一个需要换行符的文本文件格式,你会得到很简单的数据validation非常便宜:如果文件以最后没有换行的行结束,就知道文件已经损坏。 每行只有一个额外的字节,您可以高精度地检测到损坏的文件,几乎没有CPU时间。
据推测,只是一些parsing代码预计它在那里。
我不确定我会认为这是一个“规则”,这当然不是我坚持宗教的东西。 大多数明智的代码将知道如何逐行parsing文本(包括编码)(任何行结束的select),在最后一行有 – 或没有换行符。
事实上 – 如果你以一条新的路线结束:在EOL和EOF之间是否存在(理论上)空的最终线? 一个思考…
还有一个实际的编程问题,最后缺less新行的文件: read
内置的Bash(我不知道其他read
实现)不能正常工作:
printf $'foo\nbar' | while read line do echo $line done
这只打印foo
! 原因是,当read
遇到最后一行时,它将内容写入$line
但返回退出代码1,因为它到达了EOF。 这打破了while
循环,所以我们永远不会到达echo $line
部分。 如果你想处理这种情况,你必须做到以下几点:
while read line || [ -n "${line-}" ] do echo $line done < <(printf $'foo\nbar')
也就是说,如果由于文件结尾处的非空行而导致read
失败,请执行echo
。 当然,在这种情况下,输出中将不会有一个额外的换行符。
除了上述的实际原因之外,如果Unix(Thompson,Ritchie等)的创始人或者他们的Multics的前辈们认识到使用行终止符而不是行分隔符有一个理论上的理由,终止符,你可以编码所有可能的文件的行。 使用行分隔符,零行文件和包含单个空行的文件之间没有区别; 它们都被编码为包含零个字符的文件。
所以,原因是:
- 因为这是POSIX定义它的方式。
- 因为有些工具期望它或没有它的“行为不端”。 例如,如果不以换行符结束,
wc -l
将不计入最后的“行”。 - 因为它简单方便。 在Unix上,
cat
只是工作,它没有复杂的工作。 它只是复制每个文件的字节,而不需要解释。 我不认为有一个相当于cat
的DOS。 使用copy a+bc
将最终合并文件a
的最后一行和文件b
的第一行。 - 因为零线的文件(或stream)可以与一个空行的文件区分开来。
一个单独的用例:当你的文本文件是版本控制的(在这种情况下特别是在git下,但也适用于其他人)。 如果将内容添加到文件末尾,则以前最后一行的行将被编辑为包含换行符。 这意味着, blame
文件找出该行最后编辑的时间将显示文本添加,而不是之前您实际想要查看的提交。
为什么(文本)文件以换行符结束?
很多人也表示,因为:
-
许多程序行为不好,或没有它,就会失败。
-
即使是处理文件的程序也没有结尾
'\n'
,该工具的function可能不符合用户的期望 – 在这种情况下可能不清楚。 -
程序很less不允许最终的
'\n'
(我不知道任何)。
然而,这引出了下一个问题:
代码应该怎样处理没有换行符的文本文件?
-
最重要的 – 不要编写假定文本文件以换行符结尾的代码 。 假设文件符合格式导致数据损坏,黑客攻击和崩溃。 例:
// Bad code while (fgets(buf, sizeof buf, instream)) { // What happens if there is no \n, buf[] is truncated leading to who knows what buf[strlen(buf) - 1] = '\0'; // attempt to rid trailing \n ... }
-
如果最后的
'\n'
是需要的,提醒用户不要和采取的行动。 IOW,validation文件的格式。 注意:这可能包括对最大行长度,字符编码等的限制。 -
明确定义,文件,代码处理缺less的最终
'\n'
。 -
不要,尽可能生成一个文件,缺乏结尾
'\n'
。
我一直觉得这个规则来自于parsing一个没有结束换行符的文件是困难的。 也就是说,你最终会在EOL字符或EOF定义的行尾写代码。 假设一条以EOL结尾的行更简单。
不过我相信这个规则是从需要换行符的C编译器中派生出来的。 正如“在文件末尾没有换行符”的编译器警告中指出的那样,#include不会添加换行符。
我自己多年来一直在想这个。 但今天我遇到了一个很好的理由。
想象一下每行都有一个logging的文件(例如:一个CSV文件)。 计算机正在写文件末尾的logging。 但它突然坠毁。 Gee是最后一行完成? (不是一个好的情况)
但是如果我们总是终止最后一行,那么我们就会知道(只要检查最后一行是否终止)。 否则,我们可能不得不放弃最后一行,只是为了安全起见。
想象一下,正在处理文件的同时该文件仍然由另一个进程生成。
这可能与此有关? 表示文件已准备好处理的标志。
我个人喜欢源代码文件末尾的新行。
它可能源于Linux或所有UNIX系统。 我记得有编译错误(gcc,如果我没有弄错),因为源代码文件没有结束一个空的新行。 为什么这样做是让人想知道的。
恕我直言,这是个人风格和意见的问题。
在过去的一段时间里,我没有把这个换行符。 保存的字符意味着通过14.4K调制解调器更快的速度。
之后,我把这个换行符放在最后,使用shift + downarrow来select最后一行。