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

时间:2009-04-08 12:16:39

标签: file unix text-files newline

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

19 个答案:

答案 0 :(得分:1198)

因为那是how the POSIX standard defines a line

    
3.206行
    
零或更多非< 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之间的示例所示?
  3. 当然这是可解决的但你需要使cat的使用更复杂(通过添加位置命令行参数,例如cat a.txt --no-newline b.txt c.txt),现在< em> command 而不是每个单独的文件控制它与其他文件粘贴的方式。这几乎肯定不方便。

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


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

答案 1 :(得分:262)

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

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

  

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

     

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

     

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

参考:The GCC/GNU mail archive

答案 2 :(得分:101)

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

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

  

零或更多非&lt; newline&gt;的序列字符加上终止&lt;换行符&gt;字符。

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

不完整的一行:

  

一个或多个非&lt; newline&gt;的序列。文件末尾的字符。

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

文本文件:

  

包含组织为零行或多行的字符的文件。这些行不包含NUL字符,并且没有一行可以超过{LINE_MAX}个字节,包括&lt; newline&gt;字符。尽管POSIX.1-2008不区分文本文件和二进制文件(请参阅ISO C标准),但许多实用程序在操作文本文件时仅产生可预测或有意义的输出。具有此类限制的标准实用程序始终指定&#34;文本文件&#34;在他们的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

从那时起,我们可以得出,如果我们处理文件的的概念,我们可能遇到任何类型的问题的唯一时间是作为文本文件的文件(文本文件是零行或多行的组织,我们知道的行必须以&lt; newline&gt;终止)。

案例:wc -l filename

我们从wc手册中了解到:

  

一行被定义为由&lt; newline&gt;界定的字符串。字符。

对JavaScript,HTML和CSS文件有什么影响,因为它们是 text 文件?

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

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

此时我们可以自信地说,在客户端处理JS,HTML,CSS时,在EOF上跳过EOL是安全的。实际上,我们可以声明缩小其中任何一个文件,不包含&lt; newline&gt;很安全。

我们可以更进一步说,就NodeJS而言,它也不能遵守POSIX标准,因为它可以在非POSIX兼容的环境中运行。

我们还剩下什么?系统级工具。

这意味着可能出现的唯一问题是使用工具将其功能与POSIX的语义相结合(例如,wc中所示的行的定义)。

即便如此,并非所有shell都会自动粘贴到POSIX上。例如,Bash不默认为POSIX行为。有一个开关可以启用它:POSIXLY_CORRECT

关于EOL价值的思考的食物&lt; newline&gt;: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的{​​{1}}仅提及读取输入到EOF,而不是&lt; newline&gt;。请注意,cat的{​​{1}}开关也会打印出非&lt;换行符&gt;终止行(或不完整行)作为 - 计数从 1 开始(根据-n。)

  

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

既然我们了解了POSIX如何定义,那么这种行为就会变得模棱两可,或者真的不合规。

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

例如,不是使用cat,而是可以man,并且请放心,我们可能要处理的文件不会危及任务的成功,我们没有写入(例如第三方库,例如缩小的JS我们wc -l filename d) - 除非我们的意图是真正计算符合POSIX标准的

<强>结论

对于某些文本文件(例如JS,HTML和CSS),在EOF上跳过EOL将会产生负面影响(如果有的话)。如果我们依赖&lt; newline&gt;出现时,我们将工具的可靠性仅限于我们创作的文件,并将自己打开以防止第三方文件引入的潜在错误。

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

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

答案 3 :(得分:60)

可能与difference between

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

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

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

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

1 first line
2 middle line
3 last line
4

答案 4 :(得分:40)

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

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

答案 5 :(得分:18)

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

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

"No newline at end of file" compiler warning

答案 6 :(得分:12)

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

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

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

答案 7 :(得分:11)

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

所以,原因是:

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

答案 8 :(得分:10)

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

答案 9 :(得分:10)

还有一个实际的编程问题,最后缺少换行的文件:read内置的Bash(我不知道其他read实现)不能按预期工作:< / p>

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。当然,在这种情况下,输出中会有一个额外的换行符,而不在输入中。

答案 10 :(得分:9)

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

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

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

答案 11 :(得分:7)

  

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

许多人表达了,因为:

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

  2. 即使处理文件的程序缺少结尾@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_the_verge); the_verge = (WebView) findViewById(webView1); // Enable Javascript WebSettings webSettings = the_verge.getSettings(); webSettings.setJavaScriptEnabled(true); // Force links and redirects to open in the WebView instead of in a browser the_verge.setWebViewClient(new WebViewClient()); the_verge.loadUrl("http://www.theverge.com"); } ,该工具的功能也可能达不到用户的期望 - 在这个角落的情况下可能不清楚。

  3. 程序很少禁止最终'\n'(我不知道)。

  4. 然而,这引出了下一个问题:

      

    如果没有换行符,代码应如何处理文本文件?

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

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

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

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

答案 12 :(得分:6)

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

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

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

答案 13 :(得分:3)

<块引用>

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

因为这是最明智的选择。

取一个包含以下内容的文件,

one\n
two\n
three

其中 \n 表示换行符,在 Windows 上是 \r\n,返回字符后跟换行符,因为它太酷了,对吧?

这个文件有多少行? Windows 说 3,我们说 3,POSIX (Linux) 说该文件已损坏,因为它的末尾应该有一个 \n

不管怎样,你说它的最后一行是什么?我想有人同意 three 是文件的最后一行,但 POSIX 说这是一条残缺的行。

它的第二行是什么?哦,这里有第一个强separation

  • Windows 说 two,因为文件是“由换行符分隔的行”(wth?);
  • POSIX 说 two\n,并补充说这是一个真实、诚实的台词。

那么选择 Windows 的后果是什么?简单:

不能说文件是由行组成的

为什么?尝试从上一个文件中取出最后一行并复制几次......你得到了什么?这:

one\n
two\n
threethreethreethree

尝试交换第二行和第三行...你会得到这个:

one\n
threetwo\n

因此

你必须说文本文件是行和\n的交替,以一行开始,以一行结束

这是一口,对吧?

你想要另一个奇怪的结果吗?

你必须接受一个空文件(0 位)是一个单行文件,神奇的是,总是因为它们在微软很酷

这很疯狂,你不觉得吗?

选择 POSIX 的后果是什么?

顶部的文件有点残缺,我们需要一些黑客来处理它。

认真

我在前面的文本中是挑衅的,因为处理末尾缺少 \n 的文本文件会迫使您使用 ad-hoc 刻度/黑客。您总是需要在某处使用 if/else 才能使事情正常进行,其中处理残缺线的分支仅处理残缺线,所有其他行都采取另一个分支。这有点种族主义,不是吗?

我的结论

我赞成 POSIX 定义一行,原因如下:

  • 一个文件自然被认为是一系列的行
  • 一行不应该是一回事,这取决于它在文件中的位置
  • 空文件不是单行文件,加油!
  • 您不应该被迫在代码中进行黑客攻击

答案 14 :(得分:3)

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

我们要做的只是:

其中有一个示例文件:foo.txt,其中包含一些json内容。

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

该文件是在寡妇机器中创建的,并且窗口脚本正在使用powershall命令处理该文件。一切都很好。

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

[{
    someProp: value
},
{
    someProp: value

繁荣,由于JSON无效,它使其余过程失败。

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

答案 15 :(得分:3)

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

但是我认为该规则源自需要换行符的C编译器。正如“No newline at end of file” compiler warning所指出的那样,#include不会添加换行符。

答案 16 :(得分:0)

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

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

答案 17 :(得分:-4)

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

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

答案 18 :(得分:-7)

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

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

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