提问人:Will Robertson 提问时间:4/8/2009 最后编辑:mirekphdWill Robertson 更新时间:9/27/2023 访问量:386584
为什么文本文件应该以换行符结尾?
Why should text files end with a newline?
答:
大概只是一些解析代码期望它在那里。
我不确定我是否会将其视为“规则”,这当然不是我虔诚地遵守的。大多数明智的代码将知道如何逐行(任何行尾选择)解析文本(包括编码),在最后一行上有或没有换行符。
事实上 - 如果你以新行结尾:(理论上)EOL 和 EOF 之间是否有空的最后一行?值得深思...
评论
基本上,如果许多程序没有获得最终的EOL EOF,它们将无法正确处理文件。
GCC 会就此发出警告,因为它是 C 标准的一部分。(显然是第 5.1.1.2 节)
评论
我个人喜欢源代码文件末尾的新行。
就此而言,它可能起源于 Linux 或所有 UNIX 系统。我记得有编译错误(如果我没记错的话,gcc),因为源代码文件没有以空换行符结尾。为什么会这样,人们不禁要问。
每一行都应以换行符结尾,包括最后一行。如果文件的最后一行未以换行符结尾,则某些程序在处理文件的最后一行时会出现问题。
GCC 警告它不是因为它无法处理文件,而是因为它必须作为标准的一部分。
C语言标准说 不为空的源文件应以换行符结尾,换行符前不应紧跟反斜杠字符。
由于这是一个“shall”子句,因此我们必须发出违反此规则的诊断消息。
这是在 ANSI C 2.1.1.2 标准的第 1989 节中。ISO C 1999 标准(可能还有 ISO C 1990 标准)的第 5.1.1.2 节。
参考资料:GCC/GNU 邮件存档。
评论
wc -l
cat
cat
wc
这可能与以下两者的区别有关:
- 文本文件(每行都应以行尾结尾)
- 二进制文件(没有真正的“行”可言,必须保留文件的长度)
例如,如果每一行都以行尾结尾,则可以避免连接两个文本文件会使第一个运行的最后一行进入第二个运行的第一行。
此外,编辑器可以在加载时检查文件是否以行尾结尾,将其保存在其本地选项“eol”中,并在写入文件时使用它。
几年前(2005 年),许多编辑(ZDE、Eclipse、Scite 等)确实“忘记”了最终的 EOL,这并不十分受欢迎。
不仅如此,他们还错误地将最终的 EOL 解释为“开始一条新行”,实际上开始显示另一条线,就好像它已经存在一样。
与在上述编辑器之一中打开它相比,使用像 vim 这样表现良好的文本编辑器的“正确”文本文件非常明显。它在文件的实际最后一行下方显示一行额外的行。你会看到这样的东西:
1 first line
2 middle line
3 last line
4
评论
想象一下,文件正在处理中,而文件仍在由另一个进程生成。
这可能与此有关?指示文件已准备好进行处理的标志。
恕我直言,这是个人风格和意见的问题。
在过去,我没有输入换行符。保存的字符意味着通过 14.4K 调制解调器的速度更快。
稍后,我放置了该换行符,以便使用 shift+downarrow 更轻松地选择最后一行。
这起源于使用简单终端的早期。换行符用于触发传输数据的“刷新”。
今天,不再需要换行符字符。当然,如果没有换行符,许多应用程序仍然有问题,但我认为这些应用程序中的错误。
但是,如果您的文本文件格式需要换行符,则可以非常便宜地获得简单的数据验证:如果文件以末尾没有换行符的行结尾,则知道文件已损坏。每行只有一个额外的字节,您可以高精度地检测损坏的文件,并且几乎没有 CPU 时间。
评论
- 3.206线
- 零个或多个非<换行符>加上终止<换行符>字符的序列。
因此,不以换行符结尾的“行”不被视为实际行。这就是为什么某些程序在处理文件的最后一行时出现问题,如果它没有换行符终止。
遵循此约定的优点是所有 POSIX 工具都期望并使用它。例如,当将文件与 连接起来时,以换行符(及以下)结尾的文件将具有与不带 () 的文件不同的效果:cat
a.txt
c.txt
b.txt
$ more a.txt
foo
$ more b.txt
bar
$ more c.txt
baz
$ cat {a,b,c}.txt
foo
barbaz
为了保持一致性,我们遵循此规则。否则,在处理默认的 POSIX 工具时会产生额外的工作。
换个角度想:如果行不是以换行符结尾的,那么使诸如 useful 之类的命令变得更加困难:您如何制作命令来连接文件,以便cat
- 它将每个文件的开头放在一个新行上,这是您 95% 的时间想要的;但
- 它允许合并两个文件的最后一行和第一行,如上面的示例中所示,在 和 ?
b.txt
c.txt
当然,这是可以解决的,但您需要使 的使用更加复杂(例如,通过添加位置命令行参数),现在命令而不是每个单独的文件控制它如何与其他文件粘贴在一起。这几乎肯定是不方便的。cat
cat a.txt --no-newline b.txt c.txt
...或者,您需要引入一个特殊的哨兵字符来标记应该继续而不是终止的行。好吧,现在您遇到了与 POSIX 相同的情况,除了倒置(行延续而不是行终止字符)。
现在,在不符合 POSIX 标准的系统(现在主要是 Windows)上,这一点是没有意义的:文件通常不以换行符结尾,例如,行的(非正式)定义可能是“用换行符分隔的文本”(注意强调)。这是完全有效的。然而,对于结构化数据(例如编程代码),它使解析变得更加简单:这通常意味着必须重写解析器。如果解析器最初是根据 POSIX 定义编写的,那么修改令牌流而不是解析器可能更容易——换句话说,在输入的末尾添加一个“人工换行符”标记。
评论
cat
我一直认为这条规则来自解析没有结束换行符的文件很困难的日子。也就是说,您最终会编写由EOL字符或EOF定义的行尾代码。假设一行以 EOL 结尾更简单。
但是,我相信该规则源自需要换行符的 C 编译器。正如“文件末尾没有换行符”编译器警告所指出的,#include 不会添加换行符。
一些工具期望这一点。例如,期望以下内容:wc
$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1
评论
wc
wc -l
1
2
\n
还有一个实际的编程问题,文件末尾缺少换行符:内置的 Bash(我不知道其他实现)无法按预期工作:read
read
printf $'foo\nbar' | while read line
do
echo $line
done
这打印只有 foo
!原因是当遇到最后一行时,它会将内容写入但返回退出代码 1,因为它到达了 EOF。这打破了循环,因此我们永远无法到达零件。如果要处理这种情况,则必须执行以下操作:read
$line
while
echo $line
while read line || [ -n "${line-}" ]
do
echo $line
done < <(printf $'foo\nbar')
也就是说,由于文件末尾的非空行而失败,请执行 if。当然,在这种情况下,输出中将有一个额外的换行符,而该换行符不在输入中。echo
read
这个答案是对技术答案的尝试,而不是意见。
如果我们想成为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} 字节,包括 <newline> 字符。尽管 POSIX.1-2008 不区分文本文件和二进制文件(参见 ISO C 标准),但许多实用程序仅在对文本文件进行操作时才产生可预测或有意义的输出。具有此类限制的标准实用程序始终在其 STDIN 或 INPUT FILES 部分中指定“文本文件”。
来源: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397
字符串如下:
由第一个 null 字节终止并包括第一个 null 字节的连续字节序列。
来源:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396
由此,我们可以得出,我们唯一可能遇到任何类型的问题的情况是,如果我们将文件的一行或文件作为文本文件的概念进行处理(因为文本文件是零行或多行的组织,并且我们知道的一行必须以<换行符>结尾)。
举个例子:.wc -l filename
从手册中我们读到:wc
行定义为由<换行符>字符分隔的字符串。
那么,JavaScript、HTML 和 CSS 文件是文本文件有什么含义?
在浏览器、现代 IDE 和其他前端应用程序中,在 EOF 跳过 EOL 没有问题。应用程序将正确解析文件。它必须如此,因为并非所有操作系统都符合 POSIX 标准,因此非操作系统工具(例如浏览器)根据 POSIX 标准(或任何操作系统级标准)处理文件是不切实际的。
因此,我们可以相对确信,EOF的EOL在应用程序级别几乎不会产生负面影响 - 无论它是否在UNIX操作系统上运行。
在这一点上,我们可以自信地说,在客户端处理 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
请注意,文件大小正好是其各个部分的总和。如果 JavaScript 文件的串联是 JS 文件的关注点,那么更合适的关注点是以分号开头每个 JavaScript 文件。cat
正如其他人在此线程中提到的:如果您想要两个输出仅为一行而不是两行的文件怎么办?换句话说,做它应该做的事情。cat
cat
of 只提到读取 EOF 的输入,而不是<换行>。请注意,开关 还将打印出非<换行符>终止行(或不完整行)作为行 - 因为计数从 1 开始(根据 .)man
cat
-n
cat
man
-n 对输出行进行编号,从 1 开始。
现在我们了解了 POSIX 如何定义一条线,这种行为变得模棱两可,或者说实际上不合规。
了解给定工具的用途和合规性将有助于确定使用 EOL 结束文件的重要性。在 C、C++、Java (JAR) 等中...有些标准会规定一个换行符来表示有效性——JS、HTML、CSS 不存在这样的标准。
例如,与其使用 one can do ,不如放心,任务的成功不会受到我们可能想要处理的文件(例如第三方库,例如我们 d 的缩小 JS )的影响——除非我们的意图是真正计算符合 POSIX 的行。wc -l filename
awk '{x++}END{ print x}' filename
curl
结论
在现实生活中,对于某些文本文件(如 JS、HTML 和 CSS)在 EOF 中跳过 EOL 会产生负面影响(如果有的话)。如果我们依赖于<换行符>的存在,我们将工具的可靠性限制在我们创作的文件上,并使自己容易受到第三方文件引入的潜在错误的影响。
故事的寓意:工程师工具没有在EOF上依赖EOL的弱点。
请随时发布适用于 JS、HTML 和 CSS 的用例,我们可以在其中研究跳过 EOL 如何产生不利影响。
评论
newline
wc -l
diff
\ No newline at end of file
为什么(文本)文件应该以换行符结尾?
正如许多人所表达的那样,因为:
许多程序表现不佳,或者没有它就无法失败。
即使是很好地处理文件的程序也缺乏结尾,该工具的功能可能无法满足用户的期望 - 在这种极端情况下可能不清楚。
'\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 ... }
如果需要最后的尾随,请提醒用户它的缺失和所采取的操作。IOW,验证文件的格式。注意:这可能包括对最大行长、字符编码等的限制。
'\n'
明确定义,文档,代码对缺失的最终的处理。
'\n'
尽可能不要生成缺少结尾的文件。
'\n'
除了上述实际原因之外,如果 Unix 的创始人(Thompson、Ritchie 等人)或他们的 Multics 前辈意识到使用行终止符而不是行分隔符是有理论原因的,我不会感到惊讶:使用行终止符,您可以对所有可能的行文件进行编码。使用行分隔符,零行文件和包含单个空行的文件之间没有区别;它们都被编码为包含零个字符的文件。
所以,原因是:
- 因为这就是POSIX定义它的方式。
- 因为有些工具期望它或没有它“行为不端”。例如,如果最后一个“行”不以换行符结尾,则不会计算该行。
wc -l
- 因为它简单方便。在 Unix 上,它只是工作,而且它工作起来没有复杂性。它只是复制每个文件的字节,不需要任何解释。我不认为有 DOS 等效于 .使用 将最终将文件的最后一行与文件的第一行合并。
cat
cat
copy a+b c
a
b
- 因为零行的文件(或流)可以与一空行的文件区分开来。
多年来,我自己也一直在思考这个问题。但我今天遇到了一个很好的理由。
想象一个每行都有一条记录的文件(例如:CSV 文件)。并且计算机正在文件末尾写入记录。但它突然崩溃了。哎呀,最后一行完成了吗?(不是一个好情况)
但是,如果我们总是终止最后一行,那么我们就会知道(只需检查最后一行是否终止)。否则,为了安全起见,我们可能每次都不得不丢弃最后一行。
评论
一个单独的用例:提交卫生,当您的文本文件受到版本控制时。
如果将内容添加到文件末尾,则之前作为最后一行的行将被编辑为包含换行符。这意味着,在文件中找出该行上次编辑的时间将显示换行符添加,而不是您真正想要查看的提交。blame
(该示例特定于 git,但相同的方法也适用于其他版本控制系统。
评论
\n
现在已经很晚了,但我只是在文件处理中遇到了一个错误,这是因为文件没有以空换行符结尾。我们正在处理文本文件,并省略了输出中的最后一行,这会导致无效的 json 结构并将进程的其余部分发送到失败状态。sed
sed
我们所做的只是:
有一个示例文件说:里面有一些内容。foo.txt
json
[{
someProp: value
},
{
someProp: value
}] <-- No newline here
该文件是在 widows 计算机中创建的,窗口脚本使用 PowerShell 命令处理该文件。都很好。
当我们使用命令处理相同的文件时sed
sed 's|value|newValue|g' foo.txt > foo.txt.tmp
新生成的文件是
[{
someProp: value
},
{
someProp: value
和 boom,由于无效的 JSON,它使其余进程失败。
因此,最好以空换行符结束文件。
评论
echo -n foo | sed '{}'
适用于sed (GNU sed) 4.4
为什么文本文件应该以换行符结尾?
因为这是最明智的选择。
获取包含以下内容的文件,
one\n
two\n
three
其中表示换行符,在 Windows 上是 ,返回字符后跟换行符,因为它太酷了,对吧?\n
\r\n
这个文件有多少行?Windows 说 3,我们说 3,POSIX (Linux) 说文件是残缺的,因为它的末尾应该有一个。\n
无论如何,你会说它的最后一行是什么?我想任何人都同意这是文件的最后一行,但 POSIX 说这是一行残缺的行。three
它的第二行是什么?哦,这里我们有第一个强分离:
- Windows 说,因为文件是“用换行符分隔的行”(wth?
two
- POSIX说,并补充说这是一条真实、诚实的路线。
two\n
那么,选择 Windows 的后果是什么呢?简单:
你不能说一个文件是由行组成的
为什么?尝试从上一个文件中获取最后一行并复制几次......你会得到什么?这:
one\n
two\n
threethreethreethree
相反,尝试交换第二行和第三行......你会得到这个:
one\n
threetwo\n
因此
您必须说文本文件是行和 \n
s 的交替,它以一行开头,以一行结束
这很拗口,对吧?
你想要另一个奇怪的结果吗?
你必须接受一个空文件(0字节,实际上是0位)是一个单行文件,神奇的是,总是因为它们在Microsoft很酷
这很疯狂,你不觉得吗?
POSIX选择的后果是什么?
顶部的文件有点残缺,我们需要一些技巧来处理它。
认真
在前面的文本中,我是挑衅性的,因为处理缺少末尾的文本文件会迫使您使用临时的勾号/黑客来处理它们。你总是需要一个 / 某个地方来使事情正常工作,其中处理残缺线的分支只处理残缺的行,所有其他行都采用另一个分支。这有点种族主义,不是吗?\n
if
else
我的结论
出于以下原因,我赞成POSIX对行的定义:
- 文件自然而然地被设想为一系列行
- 一行不应该是一回事或另一回事,这取决于它在文件中的位置
- 空文件不是单行文件,来吧!
- 你不应该被迫在你的代码中进行黑客攻击
是的,Windows 确实鼓励您省略尾随 .如果你想要一个两行文件,你必须省略尾随,否则文本编辑器会将其显示为三行文件:\r\n
\r\n
评论
\
n 替换换行符,你会看到你的注释是如何变化的:它使 \n
的含义不正确。\n
应改称为“线标记”。我会随心所欲地回答,即使是线毒贩,它仍然是一回事。
评论