一开始对这个概念还只是有点模糊,不太在意,结果一搜索才发现,这东西太有意思了,不仅有个有趣的故事,而且本身也有很多门道,还勾起了一些之前的回忆,原来以前也跟这个问题打过交道啊。
1基本概念
控制字符 |
本义 |
|||
换行符 |
\n |
newline |
LF (Line Feed) |
光标直接往下一行(不一定是行首) |
回车符 |
\r |
return |
CR(Carriage Return) |
光标重新回到本行开头 |
基本概念如上表所示。
2由来
为什么会有这两个东西呢?它有一个有趣的传说:在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。
于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。
这个分秒级的传说没有考证,但是确实,看名字也能知道换行符确实是用来将打字纸滚动一行,回车符是用来将打印头移到行开头也就是左边界。如当前位置是第5行第6个字符,那么回车是当前位置变成第5行第1个字符,而换行是当前位置变成第6行第6个字符位置,这样在打字机上打印时,只要回车而不换行,就可以在同一行上重复打印字符。
3现在情况
后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧:
\n: UNIX系统/MAC OS X系统行末结束符
\r: MAC OS系统行末结束符
\n\r: windows系统行末结束符
也就是说在计算机上,其实只需要一个符号表示这行已经结束,光标移向下一行行首。大部分系统采用了直接使用换行符\n也就是纸滚转一行的方式,只有windows觉得按照传统来,用两个符号表示光标移向下一行行首比较妥当。
而ENTER键的定义是直接配合操作系统的,如果是UNIX就单是\n,windows就是\n\r,总之实现一个换行功能。另外,其实这里说的换行符跟word中的段落标记^p是一样的。
4问题来了
诶,这里问题就来了,既然UNIX系统中\n完全表示换行回车,那么其中\r表示什么呢?这个我并不关心。
另外既然windows系统\n\r表示换行回车,那么单独的\n和\r就还是原本的意思即换行和回车么?
这个问题我们就可以探究一下了。
如何探究,其实这也有个问题,之前没有想到,后来出问题了查了半天才明白。
因为我如果在windows系统中的txt文档中直接写\n,它会被当成两个字符读取的,并没有转义的功能,所以我准备的探究方式是通过程序向文档写入字符,这个写入的字符中没有自然换行回车,所有换行回车都是显性表示,这样我们把其他字符和单独的\n和\r和\n\r写入windows系统中的txt文档,就可以观察到windows系统中单独的\n和\r各表示什么。
接下来我我进行了实验,并对着实验结果陷入了深深思考,终于发觉了哪儿有问题。
这个设想的探索方案看起来很清晰可行,但是其实它建立在我们的一个假设上:当我们通过程序向文档写入字符时,我们认为程序会原原本本地把我们写入的字符串 ’这是第一行\n\r这是第二行\n这是长一些的第三行\r这是第四行’ 先用GBK编码成01码,然后把这段01码写入文本文档,然后当我们打开文本文档的时候,文本文档把这段01码用GBK解码并显示出来。
5实验结果和分析
但实验结果告诉我们可能并不是这样,因为我的实验结果是这样的:
1)对于\r,win7.txt不识别(就当没有一样)
2)对于\n,win7.txt识别,执行结果是:换行回车
3)对于\n\r(和\r\n),win7.txt识别,执行结果是:换行回车(很容易理解,当\r不存在直接实现\n)
这个跟设想的好像不一样,后来找到了一个可能的解释:在Windows环境中,如果输入到文本文件,在编码时,程序中的一个'\n'换行符被解释成'\r'、'\n'两个字符;相反,如果读取文本文件,文件中的相邻的两个'\r'、'\n'会合并为一个'\n'输出(这是发生在转码的时候吗)。
而此时我顺便用程序传输法做实验看了下对于python控制台输出和wxpython的文本框窗口,都是如何识别程序传送的'\r'、'\n'和’ \n\r’的。必须声明,包括上面的结果,这样子的情况都是经过程序润色的,第6节会有一些真实情况的参考。
5.2 python控制台输出
1)对于\r,识别,执行结果是:把光标移到当前这行的行首,如果接下来是字符,会同时删去这行所有字符,开始显示接下来的字符,如果接下来是\n,这行文字不会被删掉,光标到下一行行首(也就是说\r后面的字符会直接覆盖上一行字符,也就是上一个\n之前的字符)
2)对于\n,识别,执行结果是:换行回车
3)对于\n\r,识别,执行结果是:换行回车(很容易理解,当\n执行时,光标换行回车,再执行\r后,什么事情其实都没有做)
4)对于\r\n,识别,执行结果是:换行回车,就是上面提到\r接下来是\n的情况,不删字符然后换行
5.3 wxpython的文本框窗口(单行文本框)
1)对于\r,识别,执行结果是:出现一个看不到的空格,而且光标停在这个空格后面,可以往前删除掉它
2)对于\n,识别,执行结果是:出现一个看不到的空格,而且光标停在这个空格后面,可以往前删除掉它
3)对于\n\r(和\r\n),识别,执行结果是:出现两个看不到的空格,而且光标停在这两个空格后面,可以往前删除掉它
5.4 wxpython的文本框窗口(多行文本框)
1)对于\r,识别,执行结果是:换行回车
2)对于\n,识别,执行结果是:换行回车
3)对于\n\r,识别,执行结果是:两个换行回车
4)对于\r\n,识别,执行结果是:1个换行回车(这个有点怪。。)
6真实情况
0)在Windows中:
'\r' 回车,回到当前行的行首,而不会换到下一行;
'\n' 换行,换到当前位置的下一行,而不会回到行首;
1)Unix/Mac系统下的文件在Windows里打开,所有文字会变成一行
2)Windows里的文件在Unix/Mac下打开,在每行的结尾可能会多出一个^M符号
3)Linux保存的文件在windows上用记事本看的话会出现黑点
4)在linux下,命令unix2dos 是把linux文件格式转换成windows文件格式,命令dos2unix 是把windows格式转换成linux文件格式。
5)在不同平台间使用FTP软件传送文件时, 在ascii文本模式传输模式下, 一些FTP客户端程序会自动对换行格式进行转换. 经过这种传输的文件字节数可能会发生变化。 如果你不想ftp修改原文件, 可以使用bin模式(二进制模式)传输文本。
6)一个程序在windows上运行就生成CR+LF换行格式的文本文件,而在Linux上运行就生成LF格式换行的文本文件。
7其他
1)
在C语言里回车和换行是两个概念,回车是指光标由行中任意位置移动到行首,换行指换到下一行的情况。
第二个以及以后的多个(个数不定)的参数:后面的参数是用来告诉计算机在前面的%d占位符上将输出值得变量名。
我们做一个试验:printf("hello") ;
输出结果:helloPress any key to continue ...
大家会看到,这时没有了回车换行,Press any key to continue ...就跑到上一行了。
对于回车的格式有单独的格式符\r
例如:printf("abcde\rf\n");他的输出就为:
fbcde
press any key to continue ...
实际上,计算机先输出:
abcde_ (注意“_”代表光标的位置)
然后,遇到\r格式符:
abcde (注意此时,光标在字母a的下方)
在然后,输出f和\n(回车换行)
fbcde (注意此时字母f把字母a覆盖掉了)
_ (这时,光标在下一行的行首)
紧跟着,Vc++集成环境输出Press any key to continue ...字符串。
2)软硬回车
硬回车就是普通我们按回车产生的,它在换行的同时也起着段落分隔的作用。
软回车是用 Shift + Enter 产生的(在word中看到这个样子↓,在替换中名字叫手动换行符^l),它换行,但是并不换段,即前后两段文字在 Word 中属于同一“段”。在应用格式时你会体会到这一点。
我们常用的回车是硬回车,就是在word中敲击Enter键产生的那个弯曲的小箭头,占两个字节。这种回车可以有效地把段落标记出来分清楚。在两个硬回车之间的文字自成一个段落,可以对它单独设置段落标记而不用担心其他段落受到影响。这也是我们习惯用硬回车的原因:排版方便。
但是硬回车也给我们带来了麻烦。你如果是网页设计者,或者是论坛游侠,一定有这样的经历:当你打算换行时,换出的行却实在不能恭维,行间距太大了!其实这和硬回车的原理是一样的,只不过在word等文本编辑器中没有显示出它的“本来面目”。不过这样的排版的确造成了不小的困难,这时我们就得请出硬回车的兄弟:软回车。
软回车只占一个字节,在word中是一个向下的箭头。如果你从很复杂的网页中向word中复制过文字的话,对它一定不会陌生。但是想在word中直接输入软回车可不是那么容易的。因为软回车不是真正的段落标记,它只是另起了一行,不是分段。所以它不是很利于文字排版,因为它无法作为单独的一段被赋予特殊的格式。但是尽管如此,它在网页设计中还是具有举足轻重的地位的。
软回车能使前后两行的行间距大幅度缩小,因为它不是段落标记,要和法定的段落标记——硬回车区别出来。硬回车的html代码是<p>..</p>,段落的内容就夹在里面,而软回车的代码很精悍:<br>。因此在网页中想用到软回车,只需切换到代码页面,键入软回车的代码即可。
下面我讲一下不同编辑器文字互相拷贝时回车的转化情况。
地球人都知道的,网页的文字如果复制到word中,则硬回车变为弯曲的箭头,软回车变为向下的箭头。结果造成习惯用word编辑文本的朋友很不习惯很不舒服的情况。
word中的文本复制到网页中也是同样的道理。可以说word和网页比较兼容的,要不怎么会有“保存为web页”这种选项呢?
记事本也是大家摸的比较多的编辑器。但是近年来随着社会发展外加记事本的种种弊端,许多人都将其打入冷宫。对此我只能表示遗憾,因为记事本本身的功能不丰富就是别的编辑器所取代不了的优点。大家再次将网页的文字复制时,不妨粘贴到记事本里试试。哈哈,不管网页设计者用的是什么回车,现在都变成一种回车了!怎么,你不信?那就看看吧:软回车变成了普通的回车,硬回车变成了两个普通的回车。你再从记事本里复制文字到word,记事本里的回车无一例外全都变成了硬回车!你再再从记事本里复制文字到网页编辑器,所有回车就都变成软回车了!!
3)再谈文件操作时对换行回车的处理
在编程时文件操作
wb(二进制方式)或者wt(文本方式)也会有影响。我做了个实验(实验用.net2003)
在01.txt文件中输入12然后enter,在ultraedit中看到的二进制是31 32 0d 0a
然后程序如下:
int mian()
{ FILE *fp1,*fp2,*fp3,*fp4,*fp5,*fp6;
char a[10];
char b[10];
fp1 = fopen("01.txt","r");
fp3 = fopen("02.txt","w");
fread(a,sizeof(unsigned char),8,fp1); //a里是31 32 0a
fwrite(a,sizeof(unsigned char),8,fp3); //02.txt里是31 32 0d 0a ,原因是输入的情况下,换行回车转换成换行,然后输出时换行又会转成换行回车
fclose(fp1);
fclose(fp3);
fp2 = fopen("01.txt","rb");
fp4 = fopen("03.txt","wb");
fread(b,sizeof(unsigned char),8,fp2); //b里是31 32 0d 0a
fwrite(b,sizeof(unsigned char),8,fp4); //03.txt里是31 32 0d 0a ,原因是二进制情况下回车和换行的(类似文本方式的那种转换)是不存在的
fclose(fp2);
fclose(fp4);
return 0;
}
好像结论是这样的:读的方式下,在文本方式下,enter是0x0a;在二进制方式下,enter是0x0d,0x0a。
MSDN中查到这样的话:Also, in text mode, carriage return–linefeed combinations are
translated into single linefeeds on input, and linefeed characters are
translated to carriage return–linefeed combinations on output. (输入的情况下,换行回车转换成换行,然后输出时换行又会转成换行回车)When a Unicode stream-I/O function operates in text mode (the default),
the source or destination stream is assumed to be a sequence of multibyte
characters. Therefore, the Unicode stream-input functions convert multibyte
characters to wide characters. For the same reason, the Unicode stream-output
functions convert wide characters to multibyte characters.
Open in binary (untranslated) mode; translations involving carriage-return and
linefeed characters are suppressed. (二进制情况下回车和换行的转换是不存在的).