你是否也和我一样曾经好奇过为什么回车叫做回车呢?回车回的是哪门子的车,哪来的车?你是否知道回车和换行的区别呢?
前传
在展开这个话题之前先说一个身边的故事。下面一段代码是同事写来处理一个简单的文件然后输出到另一个文件的代码,大家觉得有什么问题么?
string content; using (StreamReader sr = new StreamReader("a.txt")) { content = sr.ReadToEnd(); } string[] rows = content.Split(‘\n‘); string result = string.Empty; using (StreamWriter sw = new StreamWriter("b.txt")) { foreach (var row in rows) { //处理row sw.WriteLine(row); } }
最后发现b.txt在某一些编辑器中每一行中间都多了一个空行,但是代码里明明是每一行写一次的,怎么会出现空行呢?
问题就处在content.Split(‘\n‘);
这句话,因为在Windows下文件的行末包含两个字符\r\n
,而不是仅仅是\n
。用Notepad++打开一个文件看一下一目了然了:
回车和换行前世今生
回到最开始的那个问题,换行符尚可理解,但是回车符这名字好奇怪,而且换一个行为毛需要两个特殊字符呢?
记得前几天看过一篇关于光速安振创投副总裁张矩(Google前员工)的采访:
Google认为计算机科学完全是由人基于理性的设计,和物理、化学等基础性学科不一样,它背后的原理是人可以理解的,也唯有理解了原理之后,才能学会创新。例如你需要了解TCP/IP为什么会设计成这样,而不是只知道它是什么。在Google看来,只要人的基础足够好,在他们的环境下就可以做好,而且环境变了,以前的经验未必有用。Google最早的几个员工里,也只有两个来自计算机专业。
计算机里的一些设计往往是由特定的原因或者历史问题才存在的,那么这个奇怪的回车是为什么存在呢?
从打字机说起
人们在使用最开始的打字机的时候,当打到一行末尾需要换行的时候,需要做两个操作。第一个就是拉动carriage讲纸回到行首,然后再拉动换行杆将纸张向下移动一行。这个设计影响了之后电传打印机的设计,而电传打印机的设计间接的影响了最开始计算机系统中的一部分设计(因为最开始的计算机需要兼容电传打印机)。
打印机的那个装置叫做Carriage,于是回到行首的这个操作就叫做Carriage Return,翻译成中文就变成了回车,这里的车其实是打印机上的一个装置。后来的打字机则将这两个操作合并到了一个操作装置上去了。
ASCII码的设计
大家都知道\r
和\n
是包含在ASCII码中的,ASCII是由ISO和ASA(ASNI的前身)同时设计的,在ISO的标准草稿中支持CR
+LF
或LF
作为新行标识,而ASA的标准草稿则支持CR
+LF
。
CR
+LF
之所以同时使用是为了兼容当时的电传打印机,和老式打印机一样电传打印机需要两个指令来完成一次换行。所以后来的很多系统中都沿用了这个CR
+LF
的惯例,将它们作为新行的标识。
混乱的现状
虽然很多系统沿用了这个惯例,但是还是有很多其他的系统使用了不同的换行方式。
Windows:CR
+ LF
Unix及类Unix系统(Linux, OS X):LF
老板本
MacOS:CR
大部分的文本相关的因特网协议(Http, FTP, IRC, SMTP)都强制使用ASCII码 CR
+LF
做换行符。
这就导致了一个问题,文件如果从一个系统直接拷贝到另一个系统就需要对其中的换行符进行转换才能够正确的使用。
回车与换行的故事