标准IO库的介绍
标准IO库能够处理很多的细节,例如缓冲区的分配,以优化长度执行I/O等。这些处理使用户不必担心如何选择使用正确的块长度,这就方便了用户的使用。
1.1 流和FILE对象
所有的IO函数都是针对文件描述符的,每当打开一个文件时,就返回一个文件描述符,然后这个文件描述符就用于后面的IO操作。对于标准IO库,它的操作是围绕流(stream)来进行的。当用标准IO打开或者创建一个文件时,我们就使一个流与一个文件进行关联起来了。标准IO文件流可以用于单字节或者多字节(宽)字符集。流的定向决定了所读写字符是单字节还是多字节的,当流被创建时,它并没有定向,可以在为定向的流上使用一个多单字节或者多字节IO函数,就可以把该流的定向设置为字节定向或者宽定向。只有两个函数可以改变流的定向。分别是freopen函数和fwide函数。前者清楚流定向,后者设置流定向。
#include <stdio.h>
FILE *freopen(const char *path, const char *mode, FILE *stream);
//filename 要打开的文件名称
//mode 文件访问模式,模式如下:r,w,a等
//stream 指向FILE对象的指针,该FILE对象标识了要被重新打开的流
#include <wchar.h>
int fwide(FILE *stream, int mode);
//如果流是宽定向,则返回正值,如果是字节定向,返回负值,未定向则返回
当我们打开一个流时,标准IO函数fopen返回一个指向FILE对象的指针,该对象通常是一个结构体。它包含了标准IO库为管理该流所需要的所有信息。包含的信息:实际IO的文件描述符,指向用于该流缓冲区的指针,缓冲区的长度,当前在缓冲区的字符数以及出错标志等。为了引入一个流,需要将FILE指针作为参数传递给每一个标准IO函数。我们把FILE对象的指针叫做文件指针。
1.2 标准输出,标准输入,标准出错
对一个进程预定义了三个流,这三个分别是标准输入,标准输出,标准出错,他们可以自动地被进程使用,这三个标准IO流通过预定义文件指针stdin,stdout和stderr加以引用。他们同样定义在<stdio.h>中。
1.3 缓冲
标准IO使用缓冲的目的是避免多次使用read和write,标准IO提供了三种类型的缓冲:
1、全缓冲:填满标准IO缓冲区后才可以进行实际IO操作,对于驻留在磁盘上的文件通常是采用全缓冲的,在一个流执行第一次IO操作的时候,相关标准IO函数通常调用malloc获得需要的缓冲区。
2、行缓冲:当在输入输出中遇到换行符时,标准IO库执行IO操作。这允许我们一次输出一个字符(用标准IO函数fputc),但只有在写了一行之后才进行实际的IO操作。当流涉及到一个终端时,通常使用行缓冲。
3、不带缓冲:标准IO不对字符进行缓冲存储,例如,如果用标准IO函数fputs写15个字符到不带缓冲的流中,则函数很有可能使用write系统调用函数将这些字符立即写道相关联的打开文件上。
对于给定的任何一个流,如果我们并不喜欢这些系统默认的情况,则可以调用下面的函数更改缓冲类型:
#include <stdio.h>
void setbuf(FILE *stream, char *buf);
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
//若果成功,则返回0,出错就返回非0值
2.1 打开流
下面有三个函数打开一个标准IO流:
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fd, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
1、fopen打开一个指定的文件;
2、fdopen打开一个现有的文件描述符,并使一个标准的IO流与该描述符相结合;
3、在一个指定的流上打开一个指定的文件,如果该流已经打开,则先关闭该流,如果该流已经定向,则freopen清除该定向,此函数一般用于将一个指定的文件打开为一个预定义的流:标准输入输出,标准出错。
2.2 读和写流
一旦打开了流,就有三种不同类型的非格式化IO进行选择,对其进行读写操作:
1、每次一个字符的IO,一次读或者写一个字符,如果流失带缓冲的,则标准IO函数会处理所有缓冲。
2、每次一行的IO,如果每次想要读或者写一行,则使用fgets和fputs。每行都以一个换行符作为终止。每当调用fgets时,应说明应当处理的最大行长。
3、直接IO,fread和fwrite函数支持这种类型的IO。每次IO操作读或者写某种数量的对象,而每个对象具有指定的长度。这两个函数常用语从二级制文件中每次读或者写一个结构。
2.2.1输入函数
以下三个函数可用于一次读一个字符:
#include <stdio.h>
int getc(FILE *stream);
int fgetc(FILE *stream);
int getchar(void);
三个函数的返回值:如果成功则返回下一个字符,如已经达到文件结尾,或者出错,则返回EOF。
2.2.2输出函数
对应于上面的输入函数,就有一个与之对应的输出函数:
#include <stdio.h>
int putc(int c, FILE *stream);
int fputc(int c, FILE *stream);
int putchar(int c);
三个函数返回值:成功返回c,如果出错则返回EOF。
写函数fputc的举例,我们创建一个1.txt文件,并每次写入一个字符:
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE* fp = fopen("/home/kwb/Desktop/01stdio/sec1/1.txt","w");
if(NULL == fp)
{
printf("fopen err\n");
return 1;
}
fputc('H',fp);
fputc('e',fp);
fputc('l',fp);
fputc('l',fp);
fputc('o',fp);
fputc('!',fp);
fputc(fp);
return 0;
}
编译运行:
读文件,每次读一个字符,读上面创建的文件:
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE* fp = fopen("./1.txt","r");
if(NULL == fp)
{
printf("fopen err\n");
return 1;
}
//写文件
/*fputc('H',fp);
fputc('e',fp);
fputc('l',fp);
fputc('l',fp);
fputc('o',fp);
fputc('!',fp);*/
//读文件
int c = fgetc(fp);
printf("%c",c);
c = fgetc(fp);
printf("%c",c);
c = fgetc(fp);
printf("%c",c);
fclose(fp);
return 0;
}
执行结果:
这样去读文件肯定是有问题的,不能这么读,我们使用EOF这个宏去判断,文件是不是读完了。
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE* fp = fopen("./1.txt","r");
if(NULL == fp)
{
printf("fopen err\n");
return 1;
}
while(1)
{
int c = fgetc(fp);
if(EOF == c)
{
break;
}
printf("%c",c);
}
fclose(fp);
return 0;
}
执行结果:
总结
前面介绍了文件的读写操作。对其有了基本的了解,更多的操作与注意事项后续继续。。。。。