IO___C基础部分(EOF, 回车换行,getchar(), getch())


EOF
     EOF(End Of File)是文件读取操作所返回的一个状态,它并不是从文件中读取到的值,而是文件读取函数在读取到文件末尾或者不能再往下读取时的一个返回值。该返回值一般是-1,这也是大多数系统中为EOF宏定义的值。正因为这个特殊的返回值,使得譬如getch(),getchar(),等函数的返回值是int而不是char,这一点后面会讲到。EOF一般可以通过返回值,eof()或者feof()函数来检测。在Windows控制台界面下,可以通过 ctrl+z来实现模拟文件末尾。
      
\r\n
     即回车换行,回车是指让光标(或者文件位置指针)移到行首部,换行是让光标移到下一行。行结束在不同的系统中有着不同的表示,网上有着这样一段关于它们的来历和区别的描述:
      在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。
     于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。
这就是“换行”和“回车”的来历,从它们的英语名字上也可以看出一二。
      后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。
Unix系统里,每行结尾只有“<换行>”,即“\n”;Windows系统里面,每行结尾是“ <回车><换行>”,即“\r\n”;Mac系统里,每行结尾是“<回车>”。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
     C语言为了统一这种区别,规定文件行末尾为换行,即'\n',这也是我们用printf("\n");就能够实现回车+换行的原因。那么除了unix(unix本身就用'\n'表征行结束)的其他系统,如Windows系统中,C语言在读取文件时,如果读取到"\r\n",会舍弃回车符'\r'只用'\n'表示行结束。这里指的读取是指以文本文件的方式读取,如果以二进制读取文件,则不会作此处理(C语言保证二进制的透明性)。这也是fopen函数参数有"r"(以文本文件方式打开以便读取)和"rb"(以二进制文件打开以便读取)之分。而在unix系统中,"r"和"rb"
并无区别,即都是透明传输的。当然,C语言在写入文本文件时,也会做对应的处理("\n"->"\r\n")。
getchar()和getch()
     在明白了上面两个重要的概念后,我们选取C语言中两个字符读取函数作分析:
     getchar()我们都比较熟悉了,它从stdin指向的流(通常就是我们所说的控制台)读取一个字符,并返回该字符,如果stdin指向的流处于文件结束处,那么它返回-1。前面曾提到,由于EOF的特殊值,使得getchar的返回值是int而不是char,而由于C系隐式转换,这会带来不少麻烦之处。
     例如
     char c;
     while((c = getchar())!=EOF)
          putchar(c);    
     咋一看并无问题,程序也的确能在getchar返回-1时退出,因为c为signed char ,接收到int型-1(32个1)截断后为0xFF,在c==EOF中,又会将c隐式转换成int,由于c是有符号的,所以扩展位后又回到0xFFFFFFFF,因此c仍然等于-1。但是这是有隐患的,当getchar得到的字符0xFF并返回0x000000FF时,返回值转换成char得到0xFF,这也等于-1即EOF, 这会使得循环提前退出。显然并非程序本意。
     你或许想将c定义为unsiged char来避免0xFF==-1 再如:
     unsigned char c;
     while(( c = getchar() ) != EOF)
          putchar(c);
      这又将导致另一个隐患:当getchar()返回-1时,转换成unsiged char为0xFF即255.当c和EOF比较时,又会转换成int,由于c是无符号的,此时(int)c为0x000000FF,即仍为255.因此循环永远也不会退出,因为unsiged char 永远得不到 -1.
     在讨论了这些过后,最后看看正确用法:
     int c;
     while(( c = getchar() ) != EOF)
          putchar(c);
     我们只考虑这里如何区分字符0xFF和返回值EOF(0xFFFFFFFF)。当读取到字符0xFF时,getchar返回0x000000FF c也接收到0x00000000FF,明显是不等于-1的,c在后面转换成char时,能够正确得到0xFF。当返回EOF时,getchar返回0xFFFFFFFF,这使得c的编码也为0xFFFFFFFF == -1 循环退出。
     getchar()还有一个容易给新手带来困惑的是:有时候我们只想从控制台读取一个字符,但是在输入一个字符后,getchar()却并没有立即返回,比如上面那段代码,在我们输入一个字符后,并没有putchar(c)立即输出。而是当我们输入abc按回车后,程序会立即输出abc并换行。这是因为前面提到的,getchar()返回stdin指向的流的下一个字符,这个流是有缓冲区的,当按回车时,会将输入的数据(包括回车换行)送入流缓冲中(造成这种的输入方式的解释之一是很早以前没有终端(即我们所说的控制台)的时候,数据读取都是通过文件的,而文件都是以行结尾的,因此为了提高IO效率,当遇到换行时,才将数据送入流缓冲中。)这样getchar()从流缓冲中取出字符,因此abc和换行看起来一次性的打印了出来,实际也是逐个字符读取和打印的。

     接下来看看getch()
     getch()和getchar()有三个区别:
     1.getch()并不从stdin指向的流的缓冲区中读取数据,而是直接从键盘输入读取数据,这意味着,当你输入abc时,每输入一个字符,getch()便返回该字符。
     2.getch()并不会回显字符,亦即你输入的字符并不会显示在控制台上。这使得我们可以通过getch()来在控制台程序中中断等待用户输入,比如所有控制台程序结束时都会有一个press anykey to continue.. 这便是通过:
     puts("press anykey to continue..");
     getch();
     来实现的。这样使得用户输入能够及时响应,不必每次都输入回车(区别1),并且由于输入不回显,也不会影响到程序的美观性。大家可能都试过点击控制台程序的Debug文件下的exe文件,控制台总是一闪而没,这就是因为在控制台生成的exe文件中,没有系统自动添加的getch()。
     3.getch包含在conio.h头文件中,以此也可以看出严格来说它并不是C标准IO(如getchar()一类位于stdio.h头文件)。

     我们可以在实际程序中选择这两种之一来实现对应功能,在通过getchar获取一个字符时,经常有这样的疏忽,在输入一个字母并按下Enter之后,getchar得到了该字符的值,却忘了处理流缓冲中剩下的回车换行。这时我们就可以通过getch()并增加回显来实现.

     以下是一段关于它们的一个有意思的小程序
     #include<stdio.h>                              #include<stdio.h>
     void main()                                         #include<conio.h>
     {                                                          void main()
          int c = getchar();                              {
          printf("%d", c);                                        int c = getch();
     }                                                                  printf("%d", c);
                                                                  }
     前者输出10          即'\n'                           后者输出13     即'\r'
     这个问题网上看了很多资料,都说的模模糊糊,不甚明了。我自己也不是很清楚,按照我自己的理解。回车键Enter按下时,系统会收到'\r',但是由于回车键按下的环境不同(如文本编辑环境),会使得系统对回车符的解释不同。在文本编辑环境下(如记事本,WORD,控制台),回车符会被Windows系统解释为"\r\n"即回车换行(因为Windows系统下"\r\n"代表行末尾)。这样的话,就能够解释第二个程序的输出,由于getch()从键盘捕捉输入,因此得到'\r'(我也考虑过可能getch()也会得到"\r\n",但是如果在printf后面再加一句c = getch()并输出, 并不能得到两个输出13 10  而是只得到13)。那么在回车换行符并放在C IO流缓冲中时,需要遵守C的规定,即行末尾由换行表示,亦即"\r\n"并解释为'\n'。前面也曾提到这种解释,我们可以把控制台也抽象成一种文件,事实上,C本身也将stdin stdout stderr作为文件描述符(Unix下就是0, 1, 2)提供给开发人员。我们在读控制台数据即读文件时,需要对Windows本身的行末尾进行修正。因此getchar()得到了'\n'。
     上一段说法全无根据,也很牵强。仅是本人猜想,希望理解这个的高手留言修正。
C语言IO的关键就是EOF,流缓冲,重定位,以及如何实现不同环境下的文件表示的统一等。

你可能感兴趣的:(IO___C基础部分(EOF, 回车换行,getchar(), getch()))