当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么文本文件以换行符结尾?》经验,为你挑选了13个好方法。

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



1> Konrad Rudol..:

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

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

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

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

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

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

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


不同的想法:如果换行没有终止行,那么制作cat有用的命令要困难得多:如何创建一个连接文件的命令,以便

    它将每个文件的开头放在一个新行上,这是你想要的95%的时间; 但

    它允许合并两个文件的最后一行和第一行,如上面的示例b.txtc.txt

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

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


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






@DougCoburn这个答案曾经有一个详尽的技术讨论,解释了为什么这是错误的,以及为什么POSIX做了正确的事情.不幸的是,这些评论最近被一个过分热心的主持人删除了.简而言之,它不是解析复杂性; 相反,你的定义使得以一种既有用又一致的方式创作诸如`cat`之类的工具变得更加困难.
@BT我只是指Windows来指出POSIX规则没有意义的情况(换句话说,我是在给你扔骨头)。我很高兴再也没有在讨论中提及它。但是,那么您的主张就没有意义了:在POSIX平台上,讨论具有不同行尾约定的文本文件完全没有意义,因为没有理由产生它们。有什么好处?实际上没有。—总之,我**真的**不理解这个答案(或POSIX规则)引起的仇恨。坦率地说,这是完全不合理的。
@Leon POSIX规则是关于减少边缘情况的。而且效果如此出色。我实际上对人们如何理解这一点感到茫然:这是一条线的最简单,自洽的定义。
@BT我想您是在以我更便捷的工作流程的* example *为决策背后的* reason *。不是,这只是后果。原因是POSIX规则是最简单的规则,它使解析器中的行处理最容易。我们甚至引起争论的唯一原因是Windows的处理方式有所不同,因此,有许多工具无法在POSIX文件上运行。如果每个人都使用POSIX,就不会有任何问题。但是人们抱怨POSIX,而不是Windows。
软件应该正确处理极端情况,而不是强制人们遵守这些愚蠢的规则(有很多愚蠢的事情浪费了开发人员的生命)。
尽管现在纠正起来非常不切实际,但很明显POSIX在定义界线时犯了一个错误-作为有关此问题的大量问题的证据。应该将一行定义为零个或多个以<eol>,<eof>或<eol> <eof>结尾的字符。解析器的复杂性不是一个有效的问题。尽可能将复杂性从程序员的头转移到库中。
@adjenks在此答案下,以前曾经有20条评论讨论此问题。它们已删除,未经编辑。无论如何,我刚刚编辑了答案,以解释为何POSIX的换行符定义更实用,以及如何使替代定义看起来同样方便(Doug建议的定义不够)。
@Andrew当然有这样的要求:您的规则必须在内部保持一致(否则,工具将不知道终端换行符是否表示多余的空行)。而且“用户”不会参与其中。*工具*需要产生一致的文件。工具可以同意将换行符视为行分隔符,而不是行终止符(这是您的建议,以及许多Windows工具所做的事情),但是–与您所说的相反–这并不能使任何事情变得更好,它只是使它*不同*。如您所言,如果我们不需要任何标准,那么我们将不再进行讨论。

2> Bill the Liz..:

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

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

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

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

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

参考:GCC/GNU邮件存档.


请编写好的程序,然后允许在处理时在需要的地方插入换行符,或者能够正确处理"丢失"的那些...实际上,不会丢失
@BilltheLizard,有什么例子*"某些程序在处理文件的最后一行时遇到问题,如果它不是换行符"*?
@Pacerier`wc -l`如果不是换行符,则不会计算文件的最后一行.此外,如果第一个文件的最后一行不是换行终止,`cat`将加入文件的最后一行,并将下一个文件的第一行合并为一行.几乎所有寻找新行作为分隔符的程序都有可能弄乱这个问题.
@BilltheLizard,我的意思是`wc`已经[已被提及](http://stackoverflow.com/a/7741505/632951)....
@BilltheLizard,我的坏,澄清一下:如果程序没有换行终止,那么在处理文件最后一行时遇到问题的程序的例子是什么(除了那些已经在线程上被大量提及的那些例如`cat`和`wc`)?

3> 小智..:

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

如果我们想成为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它应该做什么.

所述mancat唯一提到读取输入到EOF,不<换行符>.请注意,-n切换也cat将打印出非<换行>终止行(或不完整行)作为一行 - 计数从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标准的.

结论

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

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

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



4> VonC..:

它可能与以下区别有关:

文本文件(每行应该以行尾结束)

二进制文件(没有真正的"行"可以说,文件的长度必须保留)

如果每一行都以行尾结束,这就避免了,例如,连接两个文本文件会使第一行的最后一行进入第二行的第一行.

另外,编辑器可以在加载时检查文件是否以行尾结束,将其保存在本地选项'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仍然存在这个问题.

5> Flimm..:

有些工具期待这一点.例如,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 ...列表很大

6> cgp..:

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

海湾合作委员会警告你,因为它是C标准的一部分.(显然是第5.1.1.2节)

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


GCC不能处理文件,它必须将警告作为C标准的一部分.

7> Robin Whittl..:

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



8> Stefan..:

这源于使用简单终端的早期阶段.换行符char用于触发传输数据的"刷新".

今天,不再需要newline char.当然,如果换行不存在,许多应用程序仍有问题,但我认为这些应用程序中存在错误.

但是,如果你有一个需要换行符的文本文件格式,那么你可以非常便宜地获得简单的数据验证:如果文件以一行最后没有换行的行结束,你知道该文件已损坏.每行只有一个额外字节,您可以高精度地检测损坏的文件,几乎没有CPU时间.


现在,EOF for*text*文件的换行符可能不是必需的,但它是一个有用的*约定*,它使大多数unix工具能够以一致的结果协同工作.这根本不是一个bug.
很多人根本不使用Unix工具,我们也不在乎.
它不仅仅是unix工具,如果它可以采用合理的文件格式,任何工具都可以更好地工作和/或编码更简单.
@MestreLion这是一套符合愚蠢标准的坏工具的无用遗产.这些[极端主义编程](http://blog.ezyang.com/2012/11/eremremist-programming/)的文物(即一切文件!一切都应该说明文!)并没有在发明之后很快就死掉,因为它们是在历史的某个时刻唯一可用的工具.C被C++取代,它不是POSIX的一部分,它在EOF中不需要EOL,并且(明显)不鼓励它使用*nix luddists.
@Sam Watkins同意具有简单定义良好的_formats_很好。但是代码仍然需要验证,而不是假设_data_是符合格式的。
@MestreLion编程越来越缺乏基于科学的知识。越来越多的未受过教育的人开始自称程序员。大公司倾向于在行业中创造更多的技术债务,以杀死小企业。这些协议就是这样创建的。有一个固执的约定“没人在乎”既没有用处也没有愚蠢。有一个约定“我们必须以不可见的字符结尾每个文件”。

9> jrw32982 sup..:

除了上述实际原因之外,如果Unix的发起者(Thompson,Ritchie等人)或他们的Multics前辈意识到理论上有理由使用行终止符而不是行分隔符,那就不会让我感到惊讶:终结器,您可以编码所有可能的行文件.对于行分隔符,零行文件和包含单个空行的文件之间没有区别; 它们都被编码为包含零个字符的文件.

所以,原因是:

    因为这是POSIX定义它的方式.

    因为有些工具在没有它的情况下期望它或"行为不端".例如,wc -l如果不以换行结束,则不会计算最终的"行".

    因为它简单方便.在Unix上,cat只是工作,它没有复杂的工作.它只是复制每个文件的字节,而不需要解释.我不认为有一个DOS相当于cat.使用copy a+b c将最终将文件的最后一行a与第一行文件合并b.

    因为可以将零行的文件(或流)与一个空行的文件区分开来.



10> Marc Gravell..:

大概只是一些解析代码期望它存在.

我不确定我会认为它是一个"规则",它肯定不是我坚持宗教的东西.最明智的代码将知道如何逐行解析文本(包括编码)(任何行结尾的选择),最后一行有或没有换行符.

确实 - 如果你以一条新线结束:理论上是否存在EOL和EOF之间的空白终点线?一个思考......


这不是一个规则,它是一种惯例:*行*是以*行尾*结尾的东西.所以不,EOL和EOF之间没有"空的最后一行".
@Sahuagin:这不是我的*视图,这是POSIX标准定义一条线的方式.具有0字节的空文件具有0行,因此没有EOL,并且文件被认为仅具有单个空行,它**需要EOL.另请注意,只有当您想要*计算文件中的行时,这才是相关的,因为显然任何编辑器都会让您"到达"下一行(或第一行),无论是否已存在EOL.
@MestreLion:但是有问题的角色没有被命名为"行尾",它被命名为"换行符"和/或"换行符号".行分隔符,而不是行终止符.结果是最后一条空行.
没有(理智)工具会将文件的最后一个EOL(CR,LF等)计为另一个空行.如果没有结束EOL,所有POSIX工具都不会将文件的最后一个字符计为一行.无论EOL字符*名称*是"换行"还是"回车"(没有名称为"换行符"),对于所有实际用途,敏感工具将其视为行*终止符*,而不是行*分隔符*.
@MestreLion,你确定"行终结者"是理智的吗?抓住一些非程序员并进行快速调查.你会很快意识到**线**的概念更接近"线分离器"的概念."行终结者"的概念[很奇怪](http://stackoverflow.com/questions/729692/why-should-files-end-with-a-newline#comment50420226_729795).

11> l0b0..:

还有一个实际的编程问题,最后缺少换行的文件:readBash内置(我不知道其他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')

也就是说,echo如果read由于文件末尾的非空行而失败.当然,在这种情况下,输出中将有一个额外的换行符不在输入中.



12> symbiont..:

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

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

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



13> chux - Reins..:

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

许多人表达了,因为:

    许多程序表现不佳,没有程序就会失败.

    即使是处理文件的程序也没有结束'\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] = '\0';  // attempt to rid trailing \n
      ...
    }
    

    如果'\n'需要最终跟踪,请提醒用户缺席并采取措施.IOWs,验证文件的格式.注意:这可能包括最大行长度,字符编码等的限制.

    明确定义,文档,代码处理缺失的决赛'\n'.

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

推荐阅读
Life一切安好
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有