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

评论

只是一个顽固的选择。它不是文件末尾的“换行”。最后一行的末尾是一个“换行符”。另外,请参阅相关问题的最佳答案:stackoverflow.com/questions/16222530/…

只是为了挑剔,他实际上并没有写“换行”,而是写了“换行”,这是正确的。

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

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

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

#1 楼

因为这是POSIX标准定义行的方式:



3.206 Line
零个或多个非字符加上终止的的序列字符。



因此,未以换行符结尾的行不视为实际行。这就是为什么某些程序在未以换行符终止的情况下处理文件的最后一行时会遇到问题的原因。
在终端仿真器上工作时,该准则至少有一个硬性优势:所有Unix工具都希望这样做约定并使用它。例如,用cat连接文件时,用换行符终止的文件与不使用more的文件具有不同的效果:

$ more a.txt
foo
$ more b.txt
bar$ more c.txt
baz
$ cat {a,b,c}.txt
foo
barbaz


并且,如前面的示例所示,何时如果在命令行上显示文件(例如,通过cat),则以换行符结尾的文件将显示正确。终止不正确的文件可能会出现乱码(第二行)。

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


换个角度思考:如果行不是由换行符终止的,那么使诸如b.txt之类的命令变得有用得多:如何使一个命令连接文件以使之


将每个文件的开头放在新的一行,这是您95%的时间所需要的;但是
它允许合并两个文件的最后一行和第一行,如上例中在c.txtcat之间的示例?

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

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


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

评论


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

– Doug Coburn
18/12/6在18:11

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

–康拉德·鲁道夫(Konrad Rudolph)
18/12/6在18:22



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

–康拉德·鲁道夫(Konrad Rudolph)
19年2月12日在11:30

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

–康拉德·鲁道夫(Konrad Rudolph)
19年2月12日在11:32



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

–康拉德·鲁道夫(Konrad Rudolph)
19-2-14在10:33



#2 楼

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

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


C语言标准说
源文件中不为空的文件应以换行符结尾,且不得紧接反斜杠字符。

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

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


参考:GCC / GNU邮件归档。

评论


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

–啤酒
15年2月13日在16:12

@BilltheLizard,“一些程序在文件的最后一行没有以换行符结尾的情况下会出现问题”是什么例子?

–起搏器
15年7月3日在4:39

@Pacerier wc -l如果未以换行符终止,则不会计算文件的最后一行。同样,如果第一个文件的最后一行不是换行符终止,cat会将文件的最后一行与下一个文件的第一行合并为一个。几乎所有正在寻找换行符作为分隔符的程序都可能将其弄乱。

–比尔蜥蜴
2015年7月3日,11:17

@BilltheLizard,我的意思是已经提到了wc...。

–起搏器
15年7月3日在14:38

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

–起搏器
15年7月4日在5:52

#3 楼

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

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


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


来源:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html #tag_03_206

不完整的行,如:


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


来源:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195

文本文件为:


包含以零行或更多行组织的字符的文件。这些行不包含NUL字符,并且长度不能超过{LINE_MAX}个字节,包括字符。尽管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

然后,我们可以得出这唯一的时间可能遇到的任何类型的问题是,如果我们处理文件行或文本文件行的概念(因为文本文件是零行或更多行的组织,并且我们知道的行必须以)。

案例:wc -l filename

wc的手册中,我们读到:


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


如果它们是文本文件,对JavaScript,HTML和CSS文件有何影响?

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

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

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

我们可以更进一步,并说就NodeJS而言,它也不能遵守POSIX标准是它可以在不符合POSIX的环境中运行。

那又剩下什么呢?系统级工具。

这意味着可能出现的唯一问题是那些努力将其功能坚持POSIX语义的工具(例如,wc中所示的行定义)。

即使如此,并非所有shell都会自动遵守POSIX。例如,Bash不默认为POSIX行为。有一个启用它的开关:POSIXLY_CORRECT

对EOL作为的价值的思考: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的输入,而不是。请注意,-ncat开关还将打印出非终止的行(或不完整的行)作为一行-因为计数从1开始(根据man)。


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


现在我们了解了POSIX如何定义行,这种行为变得模棱两可,或者实际上是不合规的。 br />
了解给定工具的用途和合规性将有助于确定使用EOL结束文件的重要性。在C,C ++,Java(JAR)等环境中,某些标准将规定换行符的有效性-JS,HTML,CSS中不存在这样的标准。

例如,代替使用wc -l filename可以执行awk '{x++}END{ print x}' filename,并放心,我们可能要处理的未写文件不会危及任务的成功(例如,第三方库,例如curl d的小型JS)-除非我们的意图确实要计算在内符合POSIX标准的行。

结论

很少有现实生活中的用例,对于某些文本文件(如JS,HTML和CSS),在EOF处跳过EOL将会产生负面影响-如果有的话。如果我们依靠存在,那么我们将工具的可靠性仅限制于我们编写的文件,并应对第三方文件引入的潜在错误敞开大门。

故事的寓意:工程师工具不具有在EOF上依赖EOL的弱点。

随时发布用例,因为它们适用于JS,HTML和CSS,我们可以研究如何跳过EOL有不利影响。

评论


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

–路易斯·科罗拉多
17年11月22日在9:09



“几乎没有现实生活中的用例会跳过...”。不对。在现实生活中,我每天都要检查代码,这是浪费时间来处理无用的合并差异,这些差异是由于文件缺少结尾的换行符而引起的。为了保持一致,应正确终止每一行(甚至文件中的最后一行)。

–Dem Pilafian
11月3日,19:52



#4 楼

可能与以下内容之间的区别有关:


文本文件(每行应以行尾结尾)
二进制文件(不存在真正的“行”,并且必须保留文件的长度)

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

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

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

1 first line
2 middle line
3 last line
4


评论


+1。我在遇到这个问题时发现了这个问题。在Eclipse中显示此“伪”最后一行是非常烦人的,如果我删除了它,那么git(以及所有其他希望EOL的unix工具)会抱怨。另外,请注意,这不仅是在2005年:Eclipse 4.2 Juno仍然存在此问题。

–MestreLion
13年8月28日在8:57

@MestreLion,继续在stackoverflow.com/questions/729692/…

–起搏器
15年7月3日在14:34

#5 楼

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

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


评论


我不会说“一些”,我说大多数工具都希望文本文件具有这种功能,即使不是全部。 cat,git,diff,wc,grep,sed ...列表很大

–MestreLion
13年8月28日在9:02

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

– Guildstern
16年5月10日在11:08

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

– Flimm
16年5月10日在14:22



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

–分号
17年4月5日,下午3:17

#6 楼

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

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

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

评论


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

–比尔蜥蜴
09年4月8日在12:27

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

– Mark K Cowan
16 Sep 16'9:41

#7 楼

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

评论


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

–安德鲁(Andrew)
19年5月11日在14:52

您可以使用-w标记来忽略空格更改,但这不是默认设置。

–罗宾·惠特尔顿
19年7月10日在7:08

#8 楼

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

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

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

评论


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

–MestreLion
13年8月28日在9:08

我们许多人根本不使用Unix工具,我们也不在乎。

– DaveWalley
2014年8月5日在16:59

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

–山姆·沃特金斯(Sam Watkins)
2014年12月4日在2:25

@Sam Watkins同意具有简单定义良好的格式是好的。但是代码仍然需要验证,而不是假设数据是符合格式的。

–chux-恢复莫妮卡
2015年6月20日14:54

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

– polkovnikov.ph
16 Dec 5'在13:25



#9 楼

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

因此,原因如下:


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


#10 楼

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

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

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

#11 楼

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

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

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

评论


这不是规则,而是惯例:行是指以行尾结尾的东西。因此,在EOL和EOF之间没有“空的最后一行”。

–MestreLion
13年8月28日在9:11

@MestreLion:但是有问题的字符没有被命名为“行尾”,而是被命名为“换行符”和/或“换行符”。行分隔符,而不是行终止符。结果是最后一个空行。

– Ben Voigt
2015年6月20日在16:46



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

–MestreLion
15年6月30日在6:50

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

–起搏器
2015年7月3日14:36

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

–MestreLion
15年10月13日在12:03

#12 楼

还有一个实际的编程问题,即文件结尾没有换行符:内置的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文件末尾。自然,在这种情况下,输出中会有一个额外的换行符,而输入中没有。

#13 楼


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


很多人也表示这样,因为:


许多程序都这样做不能正常运行,否则就会失败。
即使处理文件的程序也没有结尾'\n',该工具的功能可能无法满足用户的期望-在这种情况下可能不清楚。
程序很少不允许最终的'\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] = 'q4312078q';  // attempt to rid trailing \n
  ...
}


如果需要最后一个末尾'\n',请警告用户其不存在以及已采取的措施。 IOW,确认文件的格式。注意:这可能包括对最大行长,字符编码等的限制。
明确定义,记录代码对缺少的最终'\n'的处理。
请勿尽可能地生成文件。缺少结尾'\n'


#14 楼

这里已经很晚了,但是我只是在文件处理中遇到一个错误,那是因为文件不是以空换行符结尾。我们正在使用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无效,它使其余过程失败。

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

评论


echo -n foo | sed'{}'与sed(GNU sed)4.4兼容

– Darw
10月5日10:18



#15 楼

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

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

#16 楼

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

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

#17 楼

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

它可能起源于Linux或所有UNIX系统。我记得那里有编译错误(如果我没记错的话,是gcc),因为源代码文件没有以空的新行结尾。为什么以这种方式让人感到奇怪。

#18 楼

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

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

后来,我放了换行符,这样可以更方便地使用shift + downarrow选择最后一行。