代码之家  ›  专栏  ›  技术社区  ›  Will Robertson

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

  •  1199
  • Will Robertson  · 技术社区  · 17 年前

    我想这里的每个人都熟悉这句格言,即所有文本文件都应该以换行符结尾。我知道这个“规则”很多年了,但我总是想知道为什么?

    18 回复  |  直到 8 年前
        1
  •  1603
  •   Konrad Rudolph    5 年前

    因为那是 how the POSIX standard defines a line

    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工具时会导致额外的工作。


    猫 有用的要困难得多:如何生成一个命令来连接文件,以便

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

    可解 但是你需要利用 猫 更复杂(通过添加位置命令行参数,例如。 cat a.txt --no-newline b.txt c.txt ),现在是

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


    现在,开始 系统(现在主要是Windows),这一点是没有意义的:文件通常不会以换行符结尾,例如,行的(非正式)定义可能是 通过换行符(注意重点)。这是完全正确的。然而,对于结构化数据(例如编程代码),它使解析变得更加复杂:这通常意味着必须重写解析器。如果最初编写解析器时考虑了POSIX定义,那么修改令牌流可能比修改解析器更容易——换句话说,在输入的末尾添加一个人工换行符。

        2
  •  291
  •   Bill the Lizard    17 年前

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

    不能 处理该文件,但因为

    C语言标准说 不为空的源文件应以新行字符结尾,其前面不得紧跟反斜杠字符。

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

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

    The GCC/GNU mail archive .

        3
  •  124
  •   Deduplicator    7 年前

    如果我们想成为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

    文本文件格式为:

    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文件的含义是什么 文本 文件夹?

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

    这意味着可能出现的唯一问题是,工具努力使其功能符合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

    正如其他人在这篇文章中提到的:如果你想 猫 两个文件,其输出仅为一行而不是两行?换句话说,,

    这个 man 猫 仅提及读取输入到EOF,而不是<新线>。请注意 -n 开关 还将打印一份非<新线>端接线路(或 )作为一个 线 -因为计数是从 1. .)

    现在我们了解了POSIX如何定义 线 ,此行为变得模棱两可,或者实际上不符合要求。

    了解给定工具的用途和法规遵从性将有助于确定使用EOL结束文件的重要性。在C、C++、java(JARS)等中…一些标准将规定有效性的新行-JS、HTML和CSS不存在这样的标准。

    例如,不使用 wc-l文件名 可以 awk '{x++}END{ print x}' filename curl d) -除非我们的目的是真正算数 在POSIX兼容的意义上。

    结论

    故事的寓意是:在EOF中,工程师工具没有依赖EOL的弱点。

    请随意发布应用于JS、HTML和CSS的用例,我们可以在其中检查跳过EOL如何产生不利影响。

        4
  •  64
  •   OmarOthman Dan Story    12 年前

    difference between :

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

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

    which was not very appreciated .

    与在上面的一个编辑器中打开文本文件相比,使用“适当”的文本文件和行为良好的文本编辑器(如vim),这一点非常明显。它在文件的实际最后一行下方显示了一个额外的行。你会看到这样的情况:

    1 first line
    2 middle line
    3 last line
    4
    
        5
  •  47
  •   Flimm D. Ben Knoble    15 年前

    wc 预计:

    $ echo -n "Line not ending in a new line" | wc -l
    0
    $ echo "Line ending with a new line" | wc -l
    1
    
        6
  •  25
  •   Robin Whittleton    9 年前

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

        7
  •  23
  •   Community Mohan Dere    9 年前

    GCC警告您这一点,因为它是C标准的一部分。(第5.1.1.2节)

    "No newline at end of file" compiler warning

        8
  •  20
  •   symbiont    5 年前

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

    设想一个文件,每行都有一条记录(例如:CSV文件)。电脑在文件末尾写记录。但它突然崩溃了。哎呀,最后一行完成了吗?(情况不太好)

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

        9
  •  19
  •   Stefan    17 年前

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

    如今,不再需要换行符了。当然,如果新行不在那里,很多应用程序仍然有问题,但我会认为这些应用程序中存在bug。

    但是,如果您有一个文本文件格式 要求

        10
  •  17
  •   jrw32982    11 年前

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

    因此,原因是:

    1. 因为POSIX就是这样定义的。
    2. 因为有些工具期待它,或者没有它就“行为不端”。例如 wc -l 如果不以换行结束,则不计算最后一行。
    3. 因为它简单方便。在Unix上, cat 它工作正常,没有复杂的问题。它只复制每个文件的字节,不需要任何解释。我不认为有一个DOS等同于 . 使用 copy a+b c 将合并文件的最后一行 a 使用文件的第一行 b .
        11
  •  12
  •   l0b0    12 年前

    还有一个实际的编程问题,即文件末尾缺少换行符 read Bash内置(我不知道其他 实施)未按预期工作:

    printf $'foo\nbar' | while read line
    do
        echo $line
    done
    

    只有 foo 阅读 遇到最后一行时,它将内容写入 $line while echo $line 部分如果要处理这种情况,必须执行以下操作:

    while read line || [ -n "${line-}" ]
    do
        echo $line
    done < <(printf $'foo\nbar')
    

    也就是说,做 echo 阅读 失败,因为文件末尾有一个非空行。当然,在这种情况下,输出中会有一个不在输入中的额外换行符。

        12
  •  11
  •   Marc Gravell    17 年前

    可能只是一些解析代码期望它在那里。

    我不确定我会认为这是一个“规则”,这肯定不是我信奉的东西。最明智的代码将知道如何逐行解析文本(包括编码)(任何行尾的选择),最后一行是否换行。

    事实上,如果你以一个新行结束:在EOL和EOF之间(理论上)是否有一个空的最后一行?一个值得深思的人。。。

        13
  •  8
  •   chux    9 年前

    许多人都表达得很好,因为:

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

    2. 节目很少 “\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” .

        14
  •  6
  •   marc_s MisterSmith    6 年前

    sed 和 忽略了输出中的最后一行,这导致json结构无效,并将进程的其余部分发送到失败状态。

    我们所做的只是:

    foo.txt 有一些 json 里面的内容。

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

    该文件是在widows计算机中创建的,Windows脚本正在使用PowerShell命令处理该文件。一切都好。

    塞德 sed 's|value|newValue|g' foo.txt > foo.txt.tmp

    [{
        someProp: value
    },
    {
        someProp: value
    

    由于JSON无效,它使其余进程失败。

        15
  •  4
  •   Enlico    5 年前

    因为这是最明智的选择。

    获取包含以下内容的文件,

    one\n
    two\n
    three
    

    哪里 \n 表示换行符,在Windows上为 \r\n

    这个文件有多少行?Windows说3,我们说3,POSIX(Linux)说文件被破坏了,因为应该有一个 \n

    不管怎样,你认为它的最后一行是什么?我想大家都同意这一点 three 是文件的最后一行,但是POSIX说这是一个残缺的行。

    separation :

    • Windows说 two
    • POSIX说 two\n 他补充说,这是一条真实、诚实的路线。

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

    不能说文件是由行组成的

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

    one\n
    two\n
    threethreethreethree
    

    one\n
    threetwo\n
    

    因此

    您必须说文本文件是行和行的交替 \n

    这真是太多了,对吧?

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

    你不觉得这很疯狂吗?

    POSIX选择的结果是什么?

    严肃的

    \n 最后,你不得不用自己的方式来对待他们 滴答声/劈啪声。你总是需要一个 if / else

    我的结论

    我支持POSIX对行的定义,原因如下:

    • 空文件不是单行文件,来吧!
    • 您不应该被迫对代码进行黑客攻击
        16
  •  3
  •   Community Mohan Dere    9 年前

    然而,我相信这条规则是从需要换行符的C编译器派生出来的。正如在 “No newline at end of file” compiler warning ,#include不会添加换行符。

        17
  •  2
  •   Pippen_001    17 年前

        18
  •  -5
  •   User    17 年前

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

        19
  •  -7
  •   Torben Gundtofte-Bruun    17 年前

    在过去,我没有写那条新线。保存一个字符意味着通过14.4K调制解调器的速度更快。

    稍后,我添加了该换行符,以便更容易使用shift+向下箭头选择最后一行。