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

余生颓废 提交于 2019-12-10 06:53:35

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


#1楼

我总是认为规则来自于解析没有结束换行符的文件很困难的日子。 也就是说,您最终会编写代码,其中行结束由EOL字符或EOF定义。 假设以EOL结束的行更简单。

但是我认为该规则源自需要换行的C编译器。 正如“文件末尾没有换行”编译器警告所指出的那样,#include不会添加换行符。


#2楼

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

如果我们想成为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标准(或任何操作系统级标准)处理文件是不切实际的。

因此,我们可以相对确信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会做它应该做的事情。

catman只提到阅读输入到EOF,而不是<newline>。 请注意, cat-n开关也会打印出非<换行>终止的行(或不完整的行 )作为一行 - 计数从1开始(根据该man说法)。

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

现在我们已经理解了POSIX如何定义一条线 ,这种行为变得模棱两可,或者真的不合规。

了解给定工具的目的和合规性将有助于确定使用EOL结束文件的重要性。 在C,C ++,Java(JAR)等中......一些标准将规定有效性的换行符 - JS,HTML,CSS没有这样的标准。

例如,不使用wc -l filename ,而是可以执行awk '{x++}END{ print x}' filename ,并确保任务的成功不会受到我们可能想要处理的文件的危害(我们没有写入)例如第三方库,例如缩小的JS,我们curl d) - 除非我们的意图是真正计算符合POSIX标准的

结论

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

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

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


#3楼

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

许多人表达了,因为:

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

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

  3. 程序很少禁止最终'\\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' ,请提醒用户缺席并采取措施。 IOWs,验证文件的格式。 注意:这可能包括最大行长度,字符编码等的限制。

  3. 明确定义,记录代码处理缺失的最终'\\n'

  4. 尽可能不生成缺少结尾'\\n'


#4楼

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

所以,原因是:

  1. 因为这是POSIX定义它的方式。
  2. 因为有些工具在没有它的情况下期望它或“行为不端”。 例如,如果wc -l没有以换行符结尾,则它不会计算最终的“行”。
  3. 因为它简单方便。 在Unix上, cat只是工作而且没有复杂功能。 它只是复制每个文件的字节,而不需要解释。 我不认为有一个DOS相当于cat 。 使用copy a+bc将最终将文件a的最后一行与文件b的第一行合并。
  4. 因为可以将零行的文件(或流)与一个空行的文件区分开来。

#5楼

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

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

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


#6楼

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


#7楼

这里已经很晚了,但我刚刚遇到一个文件处理错误,因为文件没有以空行换行结束。 我们正在使用sedsed处理文本文件,省略了输出的最后一行,导致无效的json结构并将其余进程发送到失败状态。

我们所做的只是:

有一个示例文件说: foo.txt里面有一些json内容。

[{
    someProp: value
},
{
    someProp: value
}] <-- No newline here

该文件是在widows机器中创建的,窗口脚本使用powershall命令处理该文件。 都好。

当我们使用sed命令处理相同的文件时sed sed 's|value|newValue|g' foo.txt > foo.txt.tmp新生成的文件是

[{
    someProp: value
},
{
    someProp: value

并且由于无效的JSON,它在其余的进程中失败了。

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


#8楼

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

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

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


#9楼

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

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

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


#10楼

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

它可能源于Linux或所有UNIX系统。 我记得有编译错误(gcc,如果我没有记错的话),因为源代码文件没有以空的新行结束。 为什么这样做是让人不知道的。


#11楼

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

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

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

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

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

参考: GCC / GNU邮件存档


#12楼

它可能与以下区别有关:

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

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

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

几年前(2005年),许多编辑(ZDE,Eclipse,Scite,......)确实“忘记”了最终的EOL, 这并不是很受欢迎
不仅如此,他们还错误地将最终EOL解释为“开始一条新线”,并且实际上开始显示另一条线,就好像它已经存在一样。
与在上面的一个编辑器中打开它相比,使用像vim这样表现良好的文本编辑器的“正确”文本文件非常明显。 它在文件的实际最后一行下方显示了一条额外的行。 你看到这样的事情:

1 first line
2 middle line
3 last line
4

#13楼

想象一下,当文件仍由另一个进程生成时,正在处理该文件。

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


#14楼

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

在过去,我没有把那个换行。 保存的字符意味着通过14.4K调制解调器提高速度。

后来,我把这个换行符放到了使用shift + downarrow更容易选择最后一行。


#15楼

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

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

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


#16楼

因为这是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.txtc.txt之间的例子b.txt

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

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


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


#17楼

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

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

#18楼

还有一个实际的编程问题,最后缺少换行的文件:内置的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 。 当然,在这种情况下,输出中将有一个额外的换行符不在输入中。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!