我想这里的每个人都熟悉这句话,即所有文本文件都应以换行符结尾。我已经知道这个“规则”很多年了,但我一直想知道-为什么?
我想这里的每个人都熟悉这句话,即所有文本文件都应以换行符结尾。我已经知道这个“规则”很多年了,但我一直想知道-为什么?
Answers:
因为这就是POSIX标准定义行的方式:
- 3.206线
- 零个或多个非<newline>字符加上终止的<newline>字符的序列。
因此,不以换行符结尾的行不视为实际行。这就是为什么某些程序在未以换行符终止的情况下处理文件的最后一行时遇到问题的原因。
在终端仿真器上工作时,该指南至少有一个硬性优势:所有Unix工具都希望使用此约定并可以使用它。例如,当使用串联文件时cat
,用换行符终止的文件与不使用换行符终止的文件具有不同的效果:
$ more a.txt
foo
$ more b.txt
bar$ more c.txt
baz
$ cat {a,b,c}.txt
foo
barbaz
并且,如前面的示例所示,在命令行上(例如,通过more
)显示文件时,以换行符结尾的文件会导致正确显示。终止不当的文件可能出现乱码(第二行)。
为了保持一致性,遵循此规则非常有帮助–否则,在使用默认的Unix工具时,将会引起额外的工作。
换个方式思考:如果行不是由换行符终止,那么使诸如这样的命令cat
变得困难得多:如何使命令串联文件以使
b.txt
和之间c.txt
?当然,这是可以解决的,但是您需要使用法cat
更加复杂(通过添加位置命令行参数,例如cat a.txt --no-newline b.txt c.txt
),现在该命令(而不是每个文件)控制了它如何与其他文件一起粘贴。这几乎肯定不方便。
…或者您需要引入特殊的前哨字符来标记应该继续而不是终止的行。好吧,现在您只能忍受与POSIX相同的情况,除了倒置(行连续而不是行终止符)。
现在,在不兼容POSIX的系统上(如今主要是Windows),问题很重要:文件通常不以换行符结尾,并且行的(非正式)定义例如可能是“ 由换行符分隔的文本” (注意重点)。这是完全有效的。但是,对于结构化数据(例如,编程代码),它使解析的复杂性最小化:通常,这意味着必须重写解析器。如果解析器最初是在考虑POSIX定义的情况下编写的,那么修改令牌流而不是解析器可能会更容易-换句话说,在输入的末尾添加“人工换行符”令牌。
cat
以一种既有用又一致的方式。
每行应以换行符结尾,包括最后一行。如果没有以换行符结尾,则某些程序在处理文件的最后一行时会遇到问题。
GCC警告它不是因为它不能处理文件,而是因为它必须作为标准的一部分。
C语言标准说,不为空的源文件应以换行符结尾,不得在其后立即加反斜杠字符。
由于这是一个“必须”子句,因此我们必须针对此规则的违反发出诊断消息。
这在ANSI C 1989标准的2.1.1.2节中。ISO C 1999标准(可能还有ISO C 1990标准)的5.1.1.2节。
参考:GCC / GNU邮件档案。
wc -l
如果不是换行符终止,@ Pacerier 将不计算文件的最后一行。此外,cat
如果第一个文件的最后一行不是以换行符结尾,则将文件的最后一行与下一个文件的第一行合并为一个。几乎所有正在寻找换行符作为分隔符的程序都可能将其弄乱。
cat
和wc
)?
该答案是对技术答案的尝试,而不是观点。
如果我们想成为POSIX的纯粹主义者,我们将以下行定义为:
零个或多个非<newline>字符加上终止的<newline>字符的序列。
资料来源:https : //pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206
不完整的行为:
文件末尾的一个或多个非<newline>字符序列。
资料来源:https : //pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195
文本文件为:
包含以零行或更多行组织的字符的文件。这些行不包含NUL字符,并且长度都不能超过{LINE_MAX}个字节,包括<newline>字符。尽管POSIX.1-2008不能区分文本文件和二进制文件(请参阅ISO C标准),但是许多实用程序在对文本文件进行操作时只能产生可预测或有意义的输出。具有此类限制的标准实用程序始终在其STDIN或INPUT FILES部分中指定“文本文件”。
资料来源:https : //pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397
字符串为:
由第一个空字节终止并包括第一个空字节的连续字节序列。
资料来源:https : //pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396
由此,我们可以得出唯一可能遇到任何类型问题的情况是,如果我们处理文件的一行或文件作为文本文件的概念(因为文本文件是零组织)或更多行,并且我们知道的行必须以<newline>结尾。
恰当的例子:wc -l filename
。
从wc
的手册中,我们读到:
一行定义为由<newline>字符分隔的字符串。
那么,JavaScript,HTML和CSS文件是文本 文件又意味着什么呢?
在浏览器,现代IDE和其他前端应用程序中,在EOF处跳过EOL都没有问题。应用程序将正确解析文件。由于并非所有操作系统都必须符合POSIX标准,因此非OS工具(例如浏览器)根据POSIX标准(或任何OS级标准)处理文件是不切实际的。
结果,我们可以相对确信EOF的EOL对应用程序级别几乎没有负面影响-不管它是否在UNIX OS上运行。
此时,我们可以自信地说,在客户端处理JS,HTML,CSS时,在EOF跳过EOL是安全的。实际上,我们可以说缩小这些文件中的任何一个,不包含<newline>是安全的。
我们可以更进一步,说到NodeJS,它也不能遵守POSIX标准,因为它可以在不符合POSIX的环境中运行。
那我们剩下什么呢?系统级工具。
这意味着可能出现的唯一问题是那些努力将其功能坚持POSIX语义的工具(例如,定义的行wc
)。
即使这样,并不是所有的shell都会自动遵守POSIX。例如,Bash不默认为POSIX行为。有一个启用它的开关:POSIXLY_CORRECT
。
关于EOL的价值被人们深思的内容是<newline>:https : //www.rfc-editor.org/old/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
计划执行。
所述man
的cat
唯一提到读取输入到EOF,不<换行符>。请注意,-n
of 的切换cat
还将打印出非<newline>终止的行(或不完整的行)作为一行 -因为计数从1开始(根据man
。)
-n从1开始编号输出行。
现在,我们了解了POSIX如何定义一条线,这种行为变得模棱两可,或者实际上是不合规的。
了解给定工具的用途和合规性将有助于确定使用EOL结束文件的重要性。在C,C ++,Java(JAR)等环境中,某些标准将规定换行符的有效性-JS,HTML,CSS没有此类标准。
例如,不要使用wc -l filename
一个可以做到的方法,而是要awk '{x++}END{ print x}' filename
放心,任务的成功不会受到我们未编写的文件的危害(例如,第三方库,例如我们缩小的JS curl
),除非我们目的是真正在符合POSIX的意义上计算行数。
结论
在现实生活中几乎没有用例,对于某些文本文件(如JS,HTML和CSS),如果在EOF处跳过EOL将会产生负面影响-甚至根本没有影响。如果我们依靠<newline>的存在,那么我们将工具的可靠性仅限制于我们编写的文件,并对第三方文件引入的潜在错误敞开大门。
故事的寓意是:工程师工具不具有在EOF上依赖EOL的弱点。
随意发布用例,因为它们适用于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
wc
并不期望,因为它只是在POSIX定义的“线”中起作用,而不是大多数人对“线”的直观理解。
wc -l
打印1
,但是有些人可能会说第二种情况应该打印2
。
\n
像POSIX / UNIX那样,将其视为行终止符,而不是行分隔符,那么期待第二种情况下打印2绝对是疯狂的。
这起源于使用简单终端的早期。换行符用于触发已传输数据的“刷新”。
如今,不再需要换行符。当然,如果没有换行符,许多应用程序仍然会出现问题,但是我认为这些应用程序中存在错误。
但是,如果您有需要换行符的文本文件格式,则可以很便宜地进行简单的数据验证:如果文件以结尾没有换行符的行结尾,则说明文件已损坏。每行只有一个额外的字节,您可以高精度地检测损坏的文件,几乎不需要CPU时间。
除了上述实际原因之外,如果Unix的创建者(Thompson,Ritchie等)或其Multics的前辈意识到在理论上使用行终止符而不是行分隔符,这也不会令我感到惊讶。终止符,您可以编码所有可能的行文件。使用行分隔符,零行文件和包含单个空行的文件之间没有区别。它们都被编码为包含零个字符的文件。
因此,原因是:
wc -l
如果最后一个“行”不以换行符结尾,则不算在内。cat
它可以正常工作,并且不复杂。它仅复制每个文件的字节,而无需任何解释。我认为没有DOS等效于cat
。使用copy a+b c
最终将合并文件的最后一行和文件a
的第一行b
。大概只是一些解析代码希望它在那里。
我不确定我会认为这是“规则”,而且我当然不会坚持。最明智的代码将知道如何逐行(任何行尾选择)解析文本(包括编码),最后一行上是否有换行符。
确实-如果您以新行结尾:(理论上)EOL和EOF之间是否有空的最后一行?一个要思考...
还有一个实际的编程问题,即文件结尾没有换行符:read
内置的Bash(我不知道其他read
实现)不能按预期工作:
printf $'foo\nbar' | while read line
do
echo $line
done
仅foo
打印!原因是,read
遇到最后一行时,它将内容写入其中,$line
但由于到达EOF而返回退出代码1。这打破了while
循环,所以我们再也无法发挥echo $line
作用了。如果要处理这种情况,则必须执行以下操作:
while read line || [ -n "${line-}" ]
do
echo $line
done < <(printf $'foo\nbar')
也就是说,echo
如果read
由于文件末尾的非空行而导致失败,请执行。自然,在这种情况下,输出中将有一个额外的换行符,而输入中没有。
为什么(文本)文件应以换行符结尾?
许多人也表示满意,因为:
许多程序表现不佳,否则将失败。
即使能够很好地处理文件的程序也没有结尾'\n'
,但该工具的功能可能无法满足用户的期望-在这种情况下可能不清楚。
程序很少禁止使用 final '\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,确认文件的格式。注意:这可能包括最大行长,字符编码等限制。
清楚地定义,文档,代码对缺少的final的处理'\n'
。
尽可能不要生成缺少结尾的文件'\n'
。
这里已经很晚了,但是我只是在文件处理中遇到一个错误,那是因为文件不是以空换行符结尾。我们正在使用来处理文本文件,sed
并sed
从输出中省略了最后一行,这导致了无效的json结构并将其余过程发送到失败状态。
我们正在做的是:
有一个示例文件说:foo.txt
其中包含一些json
内容。
[{
someProp: value
},
{
someProp: value
}] <-- No newline here
该文件是在寡妇机器中创建的,并且窗口脚本正在使用PowerShell命令处理该文件。都好。
当我们使用sed
命令处理相同的文件时sed 's|value|newValue|g' foo.txt > foo.txt.tmp
新生成的文件是
[{
someProp: value
},
{
someProp: value
繁荣时期,由于无效的JSON,它使其余流程失败了。
因此,以空的新行结束文件始终是一个好习惯。
我总是给人一种印象,那就是规则来自解析没有结尾换行符的文件的日子。也就是说,您最终将编写由EOL字符或EOF定义行尾的代码。假设以EOL结尾的行更简单。
但是我相信该规则是从需要换行符的C编译器派生的。并且如“在文件末尾没有换行符”编译器警告所指出的那样,#include将不会添加换行符。
假设正在处理文件,而另一个进程仍在生成文件。
可能与此有关吗?一个标志,指示文件已准备好进行处理。
恕我直言,这是个人风格和意见的问题。
在过去,我没有使用换行符。保存的字符意味着通过14.4K调制解调器的速度更快。
后来,我放置了换行符,以便更容易使用shift + downarrow选择最后一行。