即使是处理文件的程序也没有结束 '\n' ,工具's functionality may not meet the user'的预期 - 在这个角落的情况下可能不清楚 .
程序很少禁止最终 '\n' (我不知道任何) .
然而,这引出了下一个问题:
如果没有换行,代码应该对文本文件做些什么?
最重要的 - Do not write code that assumes a text file ends with a newline . 假设文件符合格式会导致数据损坏,黑客攻击和崩溃 . 例:
// 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
...
}
几年前(2005年),许多编辑(ZDE,Eclipse,Scite,......)做了"forget",最后的EOL,which was not very appreciated . 不仅如此,他们还错误地解释了最终的EOL,如'start a new line',并且实际上开始显示另一条线,就像它已经存在 . 与在上面的一个编辑器中打开文本编辑器一样,文本文件编辑器像vim这样的文本编辑器非常明显 . 它在文件的最后一行下方显示了一条额外的行 . 你看到这样的事情:
18 回答
我总是认为规则来自于解析没有结束换行符的文件很困难的日子 . 也就是说,您最终会编写代码,其中行结束由EOL字符或EOF定义 . 假设以EOL结束的行更简单 .
但是我相信该规则来自需要换行的C编译器 . 正如“No newline at end of file” compiler warning所指出的,#include不会添加换行符 .
一个单独的用例:当你的文本文件受版本控制时(在这种情况下特别是在git下,虽然它也适用于其他人) . 如果将内容添加到文件末尾,则之前最后一行的行将被编辑为包含换行符 . 这意味着
blame
文件以找出上次编辑该行的时间将显示文本添加,而不是之前您实际想要查看的提交 .我个人喜欢源代码文件末尾的新行 .
它可能源于Linux或所有UNIX系统 . 我记得有编译错误(gcc,如果我没有记错的话)因为源代码文件没有以空的新行结束 . 为什么这样做会让人不知道 .
许多人表达了,因为:
许多程序表现不佳,没有程序就会失败 .
即使是处理文件的程序也没有结束
'\n'
,工具's functionality may not meet the user'的预期 - 在这个角落的情况下可能不清楚 .程序很少禁止最终
'\n'
(我不知道任何) .然而,这引出了下一个问题:
如果需要最终尾随
'\n'
,请提醒用户缺席并采取措施 . IOWs,验证文件的格式 . 注意:这可能包括对最大行长度,字符编码等的限制 .明确定义,记录代码处理缺失的最终
'\n'
.尽可能不生成缺少结尾
'\n'
的文件 .除了上述实际原因之外,如果Unix的发起者(Thompson,Ritchie等人)或他们的Multics前辈意识到理论上有理由使用行终止符而不是行分隔符,那就不会让我感到惊讶:终结符,您可以编码所有可能的行文件 . 对于行分隔符,零行文件和包含单个空行的文件之间没有区别;它们都被编码为包含零个字符的文件 .
所以,原因是:
因为这是POSIX定义它的方式 .
因为有些工具需要它或"misbehave"没有它 . 例如,如果
wc -l
不以换行结束,则不会计算最终的"line" .因为它简单方便 . 在Unix上,
cat
只是工作,它没有复杂性 . 它只是复制每个文件的字节,而不需要解释 . 我不是't think there',相当于cat
的DOS . 使用copy a+b c
将最终将文件a
的最后一行与文件的第一行b
合并 .因为零行的文件(或流)可以与一个空行的文件区分开来 .
有些工具期待这一点 . 例如,
wc
期望这样:多年来我一直在想这个 . 但我今天遇到了一个很好的理由 .
想象一下每行都有记录的文件(例如:CSV文件) . 并且计算机正在文件末尾写入记录 . 但它突然崩溃了 . Gee是最后一行完成的? (不是很好的情况)
但是如果我们总是终止最后一行,那么我们就会知道(只需检查最后一行是否终止) . 否则我们可能不得不每次丢弃最后一行,只是为了安全起见 .
它可能与difference between有关:
文本文件(每行应该以行尾结束)
二进制文件(没有真正的"lines",并且必须保留文件的长度)
如果每一行都以行尾结束,这可以避免,例如,连接两个文本文件会使第一行的最后一行进入第二行的第一行 .
另外,编辑器可以在加载时检查文件是否以行尾结束,将其保存在本地选项'eol'中,并在写入文件时使用它 .
几年前(2005年),许多编辑(ZDE,Eclipse,Scite,......)做了"forget",最后的EOL,which was not very appreciated .
不仅如此,他们还错误地解释了最终的EOL,如'start a new line',并且实际上开始显示另一条线,就像它已经存在 .
与在上面的一个编辑器中打开文本编辑器一样,文本文件编辑器像vim这样的文本编辑器非常明显 . 它在文件的最后一行下方显示了一条额外的行 . 你看到这样的事情:
这个答案是尝试技术答案而不是意见 .
如果我们想成为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
文本文件为:
资料来源: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
从那时起,我们可以得出,我们可能遇到任何类型问题的唯一时间是我们处理文件行或文件作为文本文件的概念(因为文本文件是零组织)或更多行,我们知道的行必须以<newline>结束 .
例证:
wc -l filename
.从
wc
的手册中我们读到:对JavaScript,HTML和CSS文件有什么影响,那么它们是文本文件?
在浏览器,现代IDE和其他前端应用程序中,在EOF中跳过EOL没有问题 . 应用程序将正确解析文件 . 由于并非所有操作系统都符合POSIX标准,因此非OS工具(例如浏览器)根据POSIX标准(或任何OS级标准)处理文件是不切实际的 .
因此,我们可以相对确信EOF的EOL在应用程序级别几乎没有负面影响 - 无论它是否在UNIX OS上运行 .
在这一点上,我们可以自信地说,在客户端处理JS,HTML,CSS时,在EOF上跳过EOL是安全的 . 实际上,我们可以声明缩小其中任何一个文件,不包含<newline>是安全的 .
我们可以更进一步说,就NodeJS而言,它也不能遵守POSIX标准,因为它可以在非POSIX兼容环境中运行 .
那我们还剩下什么?系统级工具 .
这意味着可能出现的唯一问题是工具努力将其功能与POSIX的语义相结合(例如
wc
中所示的行的定义) .即便如此,并非所有shell都会自动粘附到POSIX上 . 例如,Bash不默认为POSIX行为 . 有一个开关启用它:
POSIXLY_CORRECT
.关于EOL值<newline>的思考:http://www.rfc-editor.org/EOLstory.txt
保持工具轨道,出于所有实际意图和目的,让我们考虑一下:
让我们使用没有EOL的文件 . 在撰写本文时,此示例中的文件是一个没有EOL的缩小JavaScript .
请注意
cat
文件大小正是其各个部分的总和 . 如果JS文件的串联是JS文件的一个问题,那么更合适的关注点是用分号启动每个JavaScript文件 .正如在这个帖子中提到的其他人:如果你想要两个文件,其输出只是一行而不是两个,怎么办?换句话说,
cat
做它应该做的事情 .cat
的man
仅提到读取输入到EOF,而不是<newline> . 请注意cat
的-n
开关也会打印出非<换行>终止的行(或不完整的行)作为一行 - 计数从1开始(根据man
. )现在我们已经理解了POSIX如何定义一条线,这种行为变得模糊不清,或者真的不合规 .
了解给定工具的目的和合规性将有助于确定使用EOL结束文件的重要性 . 在C,C,Java(JAR)等中......一些标准将规定有效性的换行符 - JS,HTML,CSS不存在这样的标准 .
例如,不是使用
wc -l filename
,而是可以做awk '{x++}END{ print x}' filename
,并且确保任务的成功不会受到我们可能想要处理的文件(我们没有写入的文件)的危害(例如第三方库,例如缩小的JS我们curl
d) - 除非我们的意图是真正计算符合POSIX标准的行 .Conclusion
对于某些文本文件,在EOF上跳过EOL的实际使用情况非常少如JS,HTML和CSS会产生负面影响 - 如果有的话 . 如果我们依赖<newline>存在,我们将工具的可靠性仅限于我们创作的文件,并将自己打开以防止第三方文件引入的潜在错误 .
故事的道德:在EOF中没有依赖EOL的弱点的工程师工具 .
随意发布用于JS,HTML和CSS的用例,我们可以在其中检查跳过EOL如何产生负面影响 .
想象一下,当文件仍由另一个进程生成时,正在处理该文件 .
它可能与此有关?一个标志,指示文件已准备好进行处理 .
每一行都应以换行符结尾,包括最后一行 . 某些程序在处理文件的最后一行时遇到问题,如果它不是换行符 .
GCC警告它不是因为它无法处理文件,而是因为它必须作为标准的一部分 .
参考:The GCC/GNU mail archive .
基本上有许多程序如果没有得到最终的EOL EOF,将无法正确处理文件 .
海湾合作委员会对此提出警告,因为它是C标准的一部分 . (第5.1.1.2节显然)
"No newline at end of file" compiler warning
因为那是how the POSIX standard defines a line:
因此,不以换行符结尾的行不被视为实际行 . 这就是为什么有些程序在处理文件的最后一行时遇到问题,如果它不是换行符 .
在终端模拟器上工作时,本指南至少有一个很大的优势:所有Unix工具都期望这个约定并使用它 . 例如,当使用
cat
连接文件时,以换行符终止的文件将具有与不具有以下内容的文件不同的效果:并且,如前面的示例所示,当在命令行上显示文件时(例如,通过
more
),换行符终止的文件会导致正确的显示 . 未正确终止的文件可能会出现乱码(第二行) .为了保持一致性,遵循此规则非常有用 - 否则在处理默认的Unix工具时会产生额外的工作 .
现在,在非POSIX兼容系统(现在主要是Windows)上,重点是:文件通常不以换行符结束,而行的(非正式)定义可能是“由换行符分隔的文本” (注意重点) . 这完全有效 . 然而,对于结构化数据(例如编程代码),它使解析最简单地更复杂:它通常意味着必须重写解析器 . 如果解析器最初是用POSIX定义编写的,那么修改令牌流而不是解析器可能更容易 - 换句话说,在输入的末尾添加“人工换行”令牌 .
大概只是一些解析代码期望它在那里 .
我不确定我会认为它是一个“规则”,它肯定不是我坚持宗教的东西 . 最明智的代码将知道如何逐行解析文本(包括编码)(任何行结尾选择),最后一行有或没有换行符 .
确实 - 如果你以一条新线结束:(在理论上)EOL和EOF之间是否有一条空的最后一条线?一个思考......
这源于使用简单终端的早期阶段 . 换行符char用于触发传输数据的“刷新” .
今天,不再需要newline char . 当然,如果换行不存在,许多应用程序仍然存在问题,但我认为这些应用程序中存在错误 .
但是,如果你有一个文本文件格式,你的新行,你可以非常便宜地获得简单的数据验证:如果文件以一行最后没有换行的行结束,你知道该文件已损坏 . 每行只有一个额外字节,您可以高精度地检测损坏的文件,几乎没有CPU时间 .
还有一个实际的编程问题,最后缺少换行的文件:内置的
read
Bash(我不知道其他read
实现)不能按预期工作:这只打印
foo
!原因是当read
遇到最后一行时,它将内容写入$line
但返回退出代码1,因为它达到了EOF . 这打破了while
循环,因此我们永远不会到达echo $line
部分 . 如果要处理这种情况,则必须执行以下操作:也就是说,如果
read
因为a而失败,请执行echo
文件末尾的非空行 . 当然,在这种情况下,输出中将有一个额外的换行符不在输入中 .现在已经很晚了,但我刚刚遇到一个文件处理错误,因为文件没有以空换行结尾 . 我们正在处理带有
sed
的文本文件,并且sed
省略了输出的最后一行,导致无效的json结构并将其余进程发送到失败状态 .我们所做的只是:
有一个示例文件说:
foo.txt
里面有一些json
内容 .该文件是在widows机器中创建的,窗口脚本使用powershall命令处理该文件 . 都好 .
当我们使用
sed
命令sed 's|value|newValue|g' foo.txt > foo.txt.tmp
处理相同的文件时,新生成的文件是由于无效的JSON,它在其余的进程中失败了 .
因此,使用空的新行结束文件始终是一个好习惯 .
恕我直言,这是个人风格和意见的问题 .
在过去,我没有把那个新行 . 保存的字符意味着通过14.4K调制解调器提高速度 .
后来,我把这个换行符放到了使用shift downarrow更容易选择最后一行 .