C语言简明教程(十):输入输出和文件

接上一节:分支语句和跳转语句

在Linux下一切东西都是文件,包括外设、socket等,这需要我们深入理解文件的本质,文件是一块数据块,通常不同类型的数据块有不同的数据组织格式,称为文件格式,例如exe文件属于PE格式,本文我们从文件的角度去理解C语言中的输入输出。

一、getchar、putchar输入输出

输入可分为有缓冲输入和无缓冲输入,ANSI C输入都是有缓冲的,无缓冲输入现在的计算机极少有了。缓冲输入又可分为完全缓冲和行缓冲,缓冲区填满才刷新,发送到目的地,文件输入一般都是完全缓冲,行缓冲是指出现换行符随机刷新缓冲,键盘输入是行缓冲,但是如果将键盘输入当做文件输入又可作为完全缓冲,缓冲输入的样子如下:

键盘输入缓冲解释

getchar是如何读取输入的呢?首先它会等待输入缓冲区的数据刷新,如果有刷新即一个个读取缓冲区中的字符,那么缓冲区什么时候刷新数据呢?第一种是填满缓冲区即可刷新,另一种是用户输入了换行符,结束键盘输入使用换行符是其中一个方式,缓冲区一旦刷新数据,里面就没有数据了,这时getchar读取重新等待,缓冲区等待用户输入,样子如下:

getchar读取缓冲区

那么没有缓冲是什么样子?getchar一次,putchar马上就能得到输出,如果是循环输入输出,那么用户输入abc,getchar和putchar的输出结果是aabbcc,关于输入缓冲的一个代码例子如下:

int ch;
while((ch = getchar()) != EOF){
    putchar(ch);
    putchar('\n');
    while(getchar() != '\n') // 跳过之后所有字符,造成缓冲区清空,那么第一个getchar就会停止读入转为等待
        continue;
}

二、输入输出、文件和文件结尾

有没有发现上面说到的getchar并没提到键盘,当然平时我们一般都是使用键盘输入,不然还有什么输入?键盘和屏幕可当做一种文件,文件是一种数据块,或者说所有文件对于程序来说都在一个数据池中,键盘只能作为一种只能被读取的文件,屏幕作为输出的文件,另外,硬盘上的那些“文件”同样属于文件,linux下程序通过文件描述符操作文件,文件描述符是一个文件索引值,文件描述符在所有的I/O编程都有用到,了解更多文件描述符的内容参考Linux文件描述符原理分析。另外我们的程序不直接去读取文件,而是读取缓冲区,样子如下:

程序通过缓冲区读取文件

这样你可能就会想到getchar读取缓冲区,那么输入数据的文件就不一定是键盘了。

什么时候停止读入?使用换行符?使用换行符不是一个准确的做法,因为文件本身也是可以有换行符的,这个时候我们需要一个文件结尾,来告诉程序什么时候该停止读入,在C中对于所有的正确I/O读入都可以找到一个文件结尾,包括getchar输入,这个文件结尾等于一个宏EOF,该宏在stdio.h头文件中,通常它的值是-1,但也有可能是其它值,我们只需要使用EOF即可,不用管它的值是什么。

所以当你读取网络数据、本地文件数据或者使用getchar读取输入同样可以找到文件结尾EOF,但是命令行输入不是通过输入-1来标记EOF,在UNIX或Linux使用Ctrl+D,DOS系统使用Ctrl+Z,这样程序就可以通过鉴别EOF来停止读入了,下面的例子是将标准输入输出作为文件处理,从键盘读入输出到屏幕,其中0就是标准输入的文件描述符,1是标准输出的文件描述符,另外还有一个标准错误stderr,这三个文件在程序运行的时候就会自动创建:

FILE *in = fdopen(0, "w+");
char message[40];
memset(message, 0, sizeof(message));
fread(message, sizeof(char), 4, in);
fclose(in);
FILE *out = fdopen(1, "w+");
fwrite(message, sizeof(char), strlen(message), out);
fflush(out);
fclose(out);

三、输入输出和重定向

重定向即改变输入或输出源,在上面我们看到,程序读取输入不一定是通过键盘,也可以通过本地磁盘上的文件,这个就是重定向,但是重定向本身与C编程关系不大,主要是在命令行中,重定向运算符有重定向输入<和重定向输出>,例如:pro > file,将文件file的内容作为输入到pro程序中,pro
> file,将程序的输出到file文件中,组合重定向pro
< file > newfile,将file文件内容输入到pro程序,程序输出到newfile文件,重定向输出一般会覆盖原文件,这时可使用>>追加内容到文件末尾,在命令行中还有一个常用的管道命令|,a | b,将a的输出作为b的输入。

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?