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


1466

我想这里的每个人都熟悉这句话,即所有文本文件都应以换行符结尾。我已经知道这个“规则”很多年了,但我一直想知道-为什么?


30
只是一个顽固的选择。它不是文件末尾的“换行”。最后一行的末尾是“换行”。此外,看到一个相关的问题的最佳答案:stackoverflow.com/questions/16222530/...
GCB

344
为了进一步挑剔,他实际上并没有写“换行”,而是写了“换行”,这是正确的。
sindrenm 2014年

5
不熟悉,但是想知道我确实是因为多余的换行符实际上在破坏事情的情况对我来说有点过高了
tobibeer 2015年

2
我目前正在使用Node.js流逐行解析纯文本数据,并且缺少终端换行令人讨厌,因为当流的输入端完成时,我必须添加额外的逻辑/关闭以确保最后一行得到处理。
Mark K Cowan 2015年

23
Unix在文件末尾对待其一般行为的方式如下:\ n字符不以行开头;相反,他们结束了他们。因此,\ n是行终止符,而不是行分隔符。第一行(像所有行一样)不需要\ n即可开始。最后一行(像所有行一样)需要\ n结束。文件末尾的\ n不会创建其他行。但是,有时文本编辑器会在此处添加可见的空白行。即使emacs的这样做,可选
MarkDBlackwell '16

Answers:


1380

因为这就是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变得困难得多:如何使命令串联文件以使

  1. 它将每个文件的开头放在一个新行上,这是您希望在95%的时间内完成的工作;但
  2. 它允许合并两个文件的最后一行和第一行,如上面的示例中的b.txt和之间c.txt

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

…或者您需要引入特殊的前哨字符来标记应该继续而不是终止的行。好吧,现在您只能忍受与POSIX相同的情况,除了倒置(行连续而不是行终止符)。


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


7
尽管现在进行纠正非常不切实际,但很明显POSIX在定义界线时犯了一个错误-作为有关此问题的大量问题的证据。应该将一行定义为零个或多个以<eol>,<eof>或<eol> <eof>结尾的字符。解析器的复杂性不是一个有效的问题。尽可能将复杂性从程序员的头转移到库中。
Doug Coburn '18

22
@DougCoburn这个答案曾经经过详尽的技术讨论,解释了为什么这是错误的,以及POSIX做正确的事情的原因。不幸的是,这些评论显然是由一位过分热心的主持人最近删除的。简而言之,这与解析复杂性无关;相反,您的定义使编写工具变得更加困难,例如cat以一种既有用又一致的方式。
康拉德·鲁道夫'18

8
@Leon POSIX规则是关于减少边缘情况的。而且效果如此出色。实际上,我有点茫然,人们无法理解这一点:这是一条线的最简单,自洽的定义。
康拉德·鲁道夫

6
@BT我想您是在以我更方便的工作流程为例,这是做出此决定的原因。不是,这只是后果。的原因是,POSIX规则是最简单的规则,这使得处理线解析器最容易的。我们甚至引起争论的唯一原因是Windows的处理方式有所不同,因此,有许多工具无法在POSIX文件上运行。如果每个人都使用POSIX,就不会有任何问题。但是人们抱怨POSIX,而不是Windows。
康拉德·鲁道夫

7
@BT我只是指Windows来指出POSIX规则没有意义的情况(换句话说,我是在给你扔骨头)。我很高兴再也没有在讨论中提及它。但是,那么您的主张就没有意义了:在POSIX平台上,讨论具有不同行尾约定的文本文件根本没有意义,因为没有理由产生它们。有什么好处?实际上没有。—总之,我真的不理解这个答案(或POSIX规则)引起的仇恨。坦率地说,这是完全不合理的。
Konrad Rudolph

282

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

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

C语言标准说,不为空的源文件应以换行符结尾,不得在其后立即加反斜杠字符。

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

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

参考:GCC / GNU邮件档案


17
请编写好的程序,然后要么允许在处理过程中在需要的地方插入换行符,要么能够正确处理“遗漏的”行...实际上,这并不失踪
tobibeer

4
@BilltheLizard,“一些程序在文件的最后一行是否未以换行符终止的情况下处理某些问题,这是什么?”的一些示例?
Pacerier

4
wc -l如果不是换行符终止,@ Pacerier 将不计算文件的最后一行。此外,cat如果第一个文件的最后一行不是以换行符结尾,则将文件的最后一行与下一个文件的第一行合并为一个。几乎所有正在寻找换行符作为分隔符的程序都可能将其弄乱。
比尔蜥蜴2015年

2
@BilltheLizard,我的意思是wc已经已经提到 ....
Pacerier

2
@BilltheLizard,非常糟糕,需要澄清一下:如果没有以换行符结尾的文件的最后一行,程序在处理文件最后一行时会遇到问题的一些示例(除了那些已经在线程上大量提及的文件,如catwc)?
Pacerier,2015年

116

该答案是对技术答案的尝试,而不是观点。

如果我们想成为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计划执行。

所述mancat唯一提到读取输入到EOF,不<换行符>。请注意,-nof 的切换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有何不利影响。


2
POSIX在问题中未加标签...关于MVS / OS线路末端的问题?或MS-DOS行尾?顺便说一句,所有已知的posix系统都允许文本文件不带最后一行结尾(找不到与posix兼容的声明系统的情况,在该系统中,“文本文件”在内核中经过特殊处理,可以插入适当的换行符以防万一)它)
路易斯科罗拉多州,

62

可能与以下两者之间差异有关:

  • 文本文件(每行应以行尾结尾)
  • 二进制文件(没有真正的“行”可言,并且文件的长度必须保留)

如果每行的确以行尾结尾,例如,这可以避免将两个文本文件连接在一起将使第一行的最后一行进入第二行的第一行。

另外,编辑器可以在加载时检查文件是否以行尾结尾,将其保存在其本地选项'eol'中,并在写入文件时使用它。

几年前(2005年),许多编辑人员(ZDE,Eclipse,Scite等)都“忘记”了最终的EOL,这并不是很赞赏
不仅如此,他们还错误地将最终的EOL解释为“开始新行”,并实际上开始显示另一行,就好像它已经存在一样。
与带有上述功能的文本编辑器(如vim)相比,在“适当的”文本文件中打开该文件非常明显。它在文件的最后一行下面显示了额外的一行。您会看到以下内容:

1 first line
2 middle line
3 last line
4

11
+1。我在遇到这个问题时发现了这个问题。这是烦人的Eclipse显示这个“假”的最后一行,如果我删除它,然后Git的(以及期待EOL所有其他的Unix工具)抱怨。另外,请注意,这不仅是在2005年:Eclipse 4.2 Juno仍然存在此问题。
MestreLion


46

一些工具会期望这样。例如,wc期望这样:

$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1

22
我不会说“一些”,我说大多数工具都希望文本文件(如果不是全部)。cat,git,diff,wc,grep,sed ...列表是巨大的
MestreLion 2013年

也许有人会说这wc并不期望,因为它只是在POSIX定义的“线”中起作用,而不是大多数人对“线”的直观理解。
Guildenstern

@Guildenstern直观的定义是在两种情况下都可以wc -l打印1,但是有些人可能会说第二种情况应该打印2
Flimm

@Flimm如果\n像POSIX / UNIX那样,将其视为行终止符,而不是行分隔符,那么期待第二种情况下打印2绝对是疯狂的。
分号

21

基本上,有许多程序如果没有获得最终的EOL EOF,将无法正确处理文件。

GCC警告您,因为它有望作为C标准的一部分。(显然是第5.1.1.2节)

“文件末尾没有换行符”编译器警告


5
GCC并非无法处理文件,它必须在C标准中给出警告。
比尔蜥蜴

IIRC,MSVC 2005抱怨C文件以不完整的行结尾,并可能拒绝编译它们。
Mark K Cowan

16

这起源于使用简单终端的早期。换行符用于触发已传输数据的“刷新”。

如今,不再需要换行符。当然,如果没有换行符,许多应用程序仍然会出现问题,但是我认为这些应用程序中存在错误。

但是,如果您有需要换行符的文本文件格式,则可以很便宜地进行简单的数据验证:如果文件以结尾没有换行符的行结尾,则说明文件已损坏。每行只有一个额外的字节,您可以高精度地检测损坏的文件,几乎不需要CPU时间。


15
如今,文本文件EOF上的换行符可能不是必需的,但是它是一个有用的约定,可以使大多数UNIX工具一起工作并获得一致的结果。这根本不是一个bug。
MestreLion

14
我们很多人根本不使用Unix工具,我们也不在乎。
DaveWalley 2014年

12
它不只是Unix工具,如果可以采用合理的文件格式,任何工具都可以更好地工作和/或更简单地编码。
山姆·沃特金斯

2
@Sam Watkins同意具有简单定义良好的格式是好的。但是代码仍然需要验证,而不是假设数据是符合格式的。
chux-恢复莫妮卡2015年

8
@MestreLion这是一组符合愚蠢标准的不良工具的无用遗产。这些极端主义编程产物(即所有内容!所有内容都应该使用纯文本!)在发明之后不久就消失了,因为它们是历史上某一时刻唯一可用的此类工具。C被C ++取代,它不是POSIX的一部分,在EOF上不需要EOL,并且* nix luddists显然不鼓励使用它。
polkovnikov.ph

14

一个单独的用例:当文本文件受版本控制时(在这种情况下,特别是在git下,尽管它也适用于其他文件)。如果将内容添加到文件的末尾,则之前最后一行的行将被编辑为包括换行符。这意味着blame对文件进行查找以找出上次编辑该行的时间将显示附加的文本,而不是您实际想看到的提交。


1
diff和blame应该只进行更新以检测“新行”而不是“ newlines”(\n)。问题解决了。
安德鲁

1
您可以使用-w标记忽略空白更改,但这不是默认设置。
罗宾·惠特尔顿

11

除了上述实际原因之外,如果Unix的创建者(Thompson,Ritchie等)或其Multics的前辈意识到在理论上使用行终止符而不是行分隔符,这也不会令我感到惊讶。终止符,您可以编码所有可能的行文件。使用行分隔符,零行文件和包含单个空行的文件之间没有区别。它们都被编码为包含零个字符的文件。

因此,原因是:

  1. 因为那是POSIX定义它的方式。
  2. 因为某些工具期望它或没有它就“不当行为”。例如,wc -l如果最后一个“行”不以换行符结尾,则不算在内。
  3. 因为它既简单又方便。在Unix上,cat它可以正常工作,并且不复杂。它仅复制每个文件的字节,而无需任何解释。我认为没有DOS等效于cat。使用copy a+b c最终将合并文件的最后一行和文件a的第一行b
  4. 因为可以将零行的文件(或流)与一个空行的文件区分开。

11

我多年来一直在想这个问题。但是我今天遇到了一个很好的理由。

想象一个在每一行都有记录的文件(例如:CSV文件)。并且计算机正在文件末尾写入记录。但是它突然崩溃了。哎呀,最后一行完成了吗?(不好的情况)

但是,如果我们总是终止最后一行,那么我们就会知道(只需检查最后一行是否终止)。否则,为了安全起见,我们可能每次都必须丢弃最后一行。


10

大概只是一些解析代码希望它在那里。

我不确定我会认为这是“规则”,而且我当然不会坚持。最明智的代码将知道如何逐行(任何行尾选择)解析文本(包括编码),最后一行上是否有换行符。

确实-如果您以新行结尾:(理论上)EOL和EOF之间是否有空的最后一行?一个要思考...


12
这不是一个规则,而是一个约定:一行是以end-of-line结尾的东西。因此,在EOL和EOF之间没有“空的最后一行”。
MestreLion

4
@MestreLion:但是该字符未命名为“行尾”,而是命名为“换行”和/或“换行”。行分隔符,而不是行终止符。结果是最后一个空行。
Ben Voigt

2
没有(健全的)工具会将文件的最后一个EOL(CR,LF等)计为额外的空行。如果没有结尾的EOL,则所有POSIX工具都不会将文件的最后一个字符计为一行。不管EOL字符名称是“换行”还是“回车”(没有一个名为“ newline”的字符),对于所有实用目的,明智的工具都将其视为行终止符,而不是行分隔符
MestreLion 2015年

2
@MestreLion,您确定“行终止符”是理智的吗?抓住一些非程序员并进行快速调查。您将很快意识到线的概念更接近于“线分隔符”的概念。“行终止符”的概念很奇怪
Pacerier,2015年

4
@Sahuagin:这不是我的观点,这是POSIX标准定义行的方式。一个具有0个字节的空文件有0行,因此没有EOL,并且一个文件被认为只有一个空白行,它确实需要EOL。还要注意,这仅在您要对文件的行进行计数时才有意义,因为显然任何编辑器都将允许您“转到”下一行(或第一行),无论那里是否已经有EOL。
MestreLion

10

还有一个实际的编程问题,即文件结尾没有换行符: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由于文件末尾的非空行而导致失败,请执行。自然,在这种情况下,输出中将有一个额外的换行符,而输入中没有。


9

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

许多人也表示满意,因为:

  1. 许多程序表现不佳,否则将失败。

  2. 即使能够很好地处理文件的程序也没有结尾'\n',但该工具的功能可能无法满足用户的期望-在这种情况下可能不清楚。

  3. 程序很少禁止使用 final '\n'(我不知道有什么限制)。


但这引出了下一个问题:

没有换行符的文本文件应如何处理?

  1. 最重要- 不要编写假定文本文件以换行符结尾的代码假设文件符合格式会导致数据损坏,黑客攻击和崩溃。例:

    // 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
      ...
    }
    
  2. 如果'\n'需要最后的跟踪,请警告用户其不存在以及已采取的措施。IOW,确认文件的格式。注意:这可能包括最大行长,字符编码等限制。

  3. 清楚地定义,文档,代码对缺少的final的处理'\n'

  4. 尽可能不要生成缺少结尾的文件'\n'


4

这里已经很晚了,但是我只是在文件处理中遇到一个错误,那是因为文件不是以空换行符结尾。我们正在使用来处理文本文件,sedsed从输出中省略了最后一行,这导致了无效的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,它使其余流程失败了。

因此,以空的新行结束文件始终是一个好习惯。


3

我总是给人一种印象,那就是规则来自解析没有结尾换行符的文件的日子。也就是说,您最终将编写由EOL字符或EOF定义行尾的代码。假设以EOL结尾的行更简单。

但是我相信该规则是从需要换行符的C编译器派生的。并且如“在文件末尾没有换行符”编译器警告所指出的那样,#include将不会添加换行符。


0

假设正在处理文件,而另一个进程仍在生成文件。

可能与此有关吗?一个标志,指示文件已准备好进行处理。


-4

我个人喜欢源代码文件末尾的换行符。

就此而言,它可能起源于Linux或所有UNIX系统。我记得那里有编译错误(如果我没记错的话,是gcc),因为源代码文件没有以空的新行结尾。为什么以这种方式使人们产生疑问。


-6

恕我直言,这是个人风格和意见的问题。

在过去,我没有使用换行符。保存的字符意味着通过14.4K调制解调器的速度更快。

后来,我放置了换行符,以便更容易使用shift + downarrow选择最后一行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.