第五章 标准I/O库
这一章也没什么内容,就几个函数。关于流是个什么东西我也搞不懂,应该是一个数据结构的实现吧,学了第16章会接触到一个大点儿的数据结构用来表示数据库信息,觉得它和流差不多,都是返回一个用于对数据结构操作的指针。流不是咱们写的,也不必深究,知道用FILE来操作流就行了。FILE就像一个游戏机手柄,有了手柄你就可以尽情的玩游戏了。
标准I/O是使用缓存的,最难的一点儿就是理解缓存的刷新机制,如果掌握不了,你写的程序很可能达不到你想要的效果。
概念:
标准输入stdin
标准输出stdout
标准出错srderr
我老是将这三个标准和STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO相混淆。老是等到编译程序说这个没定义啦,那个没定义啦我才改过来。既然要些笔记,就想个办法区分他们。文件描述符是一个可用的最小整数。而STDIN_FILENO是一个宏定义,它就是一个常数啦。对流进行操作的是一个FILE指针,stdin等不是宏,是一个指针变量
缓存
标准I/O缓存的目的是尽可能减少使用read和write调用的数量。在某些情况下,这是非常可怕的。缓存就像两头有个个水龙头的水池,一个进水,一个排水。为了尽可能节省人力,只有进水管将水池灌满才有人去打开那个排水管。在现面等水的人很可能会因为这段时间而渴死哦。当然,水池也有大有小。
全缓存:
行缓存:遇新行符刷新
无缓存:
刷新:在本章的意思是将缓存中的内容写到磁盘上(想不想打开排水管啊^_^)
ANSIC要求下列缓存特点:
(1)只有标准输入和标准输入并不涉及交互作用设备时,他们才是全缓存的。
(2)标准出错决不会是全缓存的。
输入 输出
字符 getc,fgetc,getchar putc,fputc,putchar
行 fgets,gets fputs,puts
块 fread fwrite
格式化 printf,fprintf,sprintf scanf,fscanf,sscanf
vprintf,vfprintf,vsprintf
函数运用:
----------------证明缓存的存在-------------------------
//----------------file1.c-------------------------
#include <stdio.h>
main()
{
printf("Hello world!");
sleep(5); //先睡眠5秒在退出
}
//---------------file2.c-------------------------
#include <stdio.h>
main()
{
printf("Hello world!/n");
sleep(5);
}
这两个程序只有一处不同,即'/n'新行符。file2.c的printf遇'/n'就立即刷新了,而file1.c在结束是才刷新的(有exit刷新,后面章节有介绍)。这种“危机”在标准I/O中处处存在,可要想好哦。
fopen 打开一指定文件
freopen 打开一指定文件并替换一指定流
fdopen 打开一文件描述符指定的文件
fclose 关闭一指定流
//------------------freopen.c----------------------
#include <stdio.h>
main()
{
creat("abc",0600);
freopen("abc","r+",stdout);//这里不会出现可执行x,注意啦r+有""
printf("Hello world!/n");
fclose(stdout);
}
//---------------------end-----------------------
king@king-laptop:~/blog$ gcc -o freopen freopen.c
king@king-laptop:~/blog$ ./freopen
king@king-laptop:~/blog$ cat abc //看到了吗,我们把printf变成了fprintf
Hello world!
king@king-laptop:~/blog$
getc,fgetc,getchar
getchar等同于getc(stdin)
getc可以实现为宏,而fgetc一定是个函数,允许将fgetc的地址作为一个参数传递(我不懂唉)
//------------典型错误---------------
//-----------getchar.c-------------
#include <stdio.h>
main()
{
char c;
while((c=getchar())!=EOF)
putchar(c);
printf("saft exit/n");
}
//-----------end----------------
看着挺不错,其实是个死循环
# define EOF (-1)
在我的系统里永远出现不了等于-1的字符
ferror 检测出错
feod 检测到达文件尾部
clearerr 清除文件表示
读字符的三个函数的出错返回与到达文件尾端的是EOF,那怎么区分的?
//----------------ferror.c-------------------
#include <stdio.h>
main()
{
FILE *fp;
char ch;
fp=fopen("abc","r");
while((ch=fgetc(fp))!=EOF)
printf("%c",ch);
if(ferror(fp))
printf("/nerror/n");//读取错误啦
if(feof(fp))
printf("/nfile end/n");//到文件尾部啦
clearerr(fp);
}
//-----------------------end-----------------
king@king-laptop:~/blog$ gcc -o ferror ferror.c
king@king-laptop:~/blog$ ./ferror
Hello world!
file end
king@king-laptop:~/blog$
ungetc
将字符回送流中,再次读取时的顺序与送回的顺序相反
回送的字符不一定是上次读到的字符
模拟机制:定义一个栈,调用ungetc就把字符压入栈,再取字符时就从栈中取,直到指针到达栈底。
也不知怎么了,我用不了这个函数,也许不支持,就不写例子了#include <stdio.h>
#include <sys/stat.h>
main()
{
int fd;
FILE *fp;
char *p="1234567890";
fd=creat("1234",0600);
write(fd,p,10);
fp=fopen("1234","r+");
getc(fp);
getc(fp);
printf("file local is %ld/n",ftell(fp));
fseek(fp,4,SEEK_CUR);
printf("after fseek get %c/n",getc(fp));
rewind(fp);
printf("file local is %ld after rewind/n",ftell(fp));
fclose(fp);
}
putc,fputc,putchar和getc,fgetc,getchar是一样的,只是方向不同,就不举例啦。
fgets
gets==fgets(stdin)
gets从标准输入读,但不推荐使用。问题是调用者在使用gets时不能指定缓存的长
度。这样就可能造成缓存越界,1988年的因特网蠕虫事件曾利用此缺陷。
-------------测试一下gets的破坏力-------------------
//--------------gets.c-----------------------
#include <stdio.h>
main()
{
char *p,*q;
p=(char *)malloc(10);
q=(char *)malloc(5);
gets(q);
printf("q=%s/n",q);//先写要被覆盖的数据
gets(p);
printf("p=%s/n",p);
printf("q=%s/n",q);//再次查看一下数据有没有被覆盖
}
//---------------------end-----------------------
king@king-laptop:~/blog$ gcc -o gets gets.c //有警告,不管他,咱干咱的
gets.c: 在函数‘main’中:
gets.c:5: 警告: 内建函数 ‘malloc’ 不兼容的隐式声明
/tmp/ccsIwCLW.o: In function `main':
gets.c:(.text+0x36): warning: the `gets'
function is dangerous and should not be used.
king@king-laptop:~/blog$ ./gets
123456
q=123456 //现在一切正常
abcdef
p=abcdef
q=123456
king@king-laptop:~/blog$ ./gets
123456
q=123456
abcdefghijklmnoporst
p=abcdefghijklmnoporst
q=orst //数据被覆盖掉了,gets真是可怕,如果按住一个键半天不撒手????????????
king@king-laptop:~/blog$
fput,puts也不说了,puts不像gets那么可怕,不过也不推荐使用
fread,fwrite
读/写块(二进制),不论是整个文件也好,一个数据结构也好,一个结构数组也好,这都可以。
//---------------fwrite.c------------------------
#include <stdio.h>
#include "ourhdr.h"
#include "err_error.h"
main()
{
FILE *fp;
char a[]={'a','b','c','d','e','f','j'};
if(creat("cba",0600)==-1)
err_sys("creat error");
if((fp=fopen("cba","r+"))==NULL)
err_sys("fopen error");
if(fwrite(&a,sizeof(char),3,fp)!=3) //记住:并不是复制3次同样的块,而是一个连续的空间
err_sys("fwrite error");
}
//------------------------end---------------------------
king@king-laptop:~/blog$ gcc -o fwrite fwrite.c
king@king-laptop:~/blog$ ./fwrite
king@king-laptop:~/blog$ cat cba
abcking@king-laptop:~/blog$ //原来不是三个a啊,我以为是三个a哩
ftell,fseek,rewind
//-------------------ftell.c---------------------
#include <stdio.h>
#include <sys/stat.h>
main()
{
int fd;
FILE *fp;
char *p="1234567890";
fd=creat("1234",0600);
write(fd,p,10);
fp=fopen("1234","r+");
getc(fp);
getc(fp);
printf("file local is %ld/n",ftell(fp));
fseek(fp,4,SEEK_CUR);
printf("after fseek getc is %c/n",getc(fp));
rewind(fp);
printf("file local is %ld after rewind/n",ftell(fp));
fclose(fp);
}
//---------------------end----------------------
king@king-laptop:~/blog$ gcc -o ftell ftell.c
king@king-laptop:~/blog$ ./ftell
file local is 2
after fseek getc is 7
file local is 0 after rewind
king@king-laptop:~/blog$
1234567890指针到了哪儿,自己数
fileno 看看程序就知啥意思,小吧
//-------------fileno.c----------------
#include <stdio.h>
main()
{
printf("STDOUT_FILENO is %d/n",fileno(stdout));
}
//----------------end-----------------
king@king-laptop:~/blog$ gcc -o fileno fileno.c
king@king-laptop:~/blog$ ./fileno
STDOUT_FILENO is 1
king@king-laptop:~/blog$