文章目录
一、标准IO介绍(写在函数之前的知识)
1、标准I/O定义
标准I/O指的是ANSI C中定义的用于I/O操作的一系列函数。只要包含C库(stdio.h)就可以调用标准I/O。
由于标准I/O封装了缓冲区,使得在读写文件的时候减少了系统调用的次数,提高了效率。
具体的可以IO介绍(一)
2、流
标准I/O的核心对象是流。当用标准I/O打开一个文件时,就会创建一个FILE结构体描述该文件。我们把这个FILE结构体称为“流”。 标准I/O函数都是基于流进行各种操作的。
typedef struct
{
short level; //缓冲区满/空的状态
unsigned flags; //文件状态标志
char fd; //文件描述符
unsigned char hold; //如缓冲区无内容则不读取字符
short bsize; //缓冲区的大小
unsigned char *buffer; //数据缓冲区的位置
unsigned char *curp; //指针当前的指向
unsigned istemp; //临时文件指示器
short token; //用于有效性检查
}FILE;
3、流的分类(Linux下没有流的差异)
流的分类分为文本流和二进制流两种:
- 文本流:文本流是由字符文件组成的序列,每一行包含0个或多个字符并以’\n’结尾。在流处理过程中所有数据以字符形式出现,‘\n’被当做回车符CR和换行符LF两个字符处理,即’\n’ASCII码存储形式是0x0D和0x0A。当输出时,0x0D和0x0A转换成’\n’
- 二进制流:二进制流是未经处理的字节组成的序列,在流处理过程中把数据当做二进制序列,若流中有字符则把字符当做ASCII码的二进制数表示。'\n’不进行变换。
在Linux/Unix系统中,文本流与二进制流没有差异, 但是在Windows中稍有差异,所以标准C库定义了两种流。
所有在Linux系统下不用考虑文本流和二进制流。
4、流的缓冲类型
1.全缓冲:当流的缓冲区无数据或无空间时才执行实际IO操作
2.行缓冲:当在输入和输出中遇到换行符(‘\n’)时,进行IO操作。
**-->标准输入/输出流(stdin/stdout)就是使用行缓冲。**
3.无缓冲:数据直接写入文件,流不进行缓冲,stderr就是无缓冲
标准I/O预定义3个流,程序运行时自动打开
标准输入流 0 stdin
标准输出流 1 stdout
标准错误输出流 2 stderr
二、标准IO(基本操作函数)
1、文件的打开:函数:fopen()
函数原型:FILE *fopen(const char *path,const char *mode)
函数参数:path:要打开的文件的路径及文件名
mode:文件打开方式,见下
函数返回值:成功:指向文件的FILE类型指针
失败:NULL
函数参数:path:要打开的文件的路径及文件名
mode:文件打开方式,见下
2、文件的关闭:函数:fclose()
函数原型:int fclose(FILE *stream)
函数参数:stream:已打开的流指针
函数返回值:成功:0
失败:EOF,并设置errno
流关闭时自动刷新缓冲区中的数据,并释放缓冲区
当一个程序正常终止时,所有打开的流都会被关闭。
流一旦关闭后就不能执行任何操作
3、示例:写一个程序,打开一个文件,然后关闭该文件。
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
if((fp = fopen("hello.txt","a+"))==NULL)//打开文件,之后判断是否打开成功
{
perror("cannot open file");
exit(0);
}
//对文件的操作
fclose(fp);//关闭文件
return 0;
}
4、错误显示函数:函数:perror
函数原型:void perror(const char *s)
函数参数:s:在标准错误流上输出的错误信息
函数返回值:无
三、标准IO(读写函数)
流支持不同的读写方式:
读写一个字符:fgetc()/fputc() 一次读/写一个字符
读写一行:fgets()和fputs()一次读/写一行
读写若干个对象:fread()/fwrite()每次读/写若干个对象,而每个对象具有相同的长度
1、单个字符输入:函数:getc()、fgetc()、getchar()
getc()函数 和 fgetc()函数是从一个指定的 流中读取一个字符, getchar()函数是从stdin中读取一个字符。
int fgetc(FILE *streanm);
int getc(FILE *stream); //宏
int getchar(void);
函数参数:stream:输入文件流
成功时返回读取的字符;
若文件到末尾或出错时返回 EOF(-1)
getchar()等同于fgetc(stdin)
getc和fgetc区别是一个是宏,一个是函数
注意:函数返回值是int类型不是char类型,主要是为了拓展返回值的范围。
示例:在屏幕上显示 D:\\demo.txt 文件的内容。
#include<stdio.h>
int main(){
FILE *fp;
char ch;
//如果文件不存在,给出提示并退出
if( (fp=fopen("D:\\demo.txt","rt")) == NULL ){
puts("Fail to open file!");
exit(0);
}
//每次读取一个字节,直到读取完毕
while( (ch=fgetc(fp)) != EOF ){
putchar(ch);
}
putchar('\n'); //输出换行符
fclose(fp);
return 0;
}
2、单个字符输出: 函数:putc()、fputc()、putchar()
putc()函数和fputc()函数是从一个指定的流中输出一个字符,putchar()函数是从stdout中输出一个字符。
函数原型:int putc(int c,FILE *stream)
int fputc(int c,FILE *stream)
int putchar(int c)
函数参数:c:待输出的字符(的ASCII码)
stream:输入文件流
函数返回值:成功:输出字符c
失败:EOF
putchar(c)等同于FPUTC(c,stdout)
注意:函数返回值是int类型不是char类型,主要是为了拓展返回值的范围。
【示例】从键盘输入一行字符,写入文件。
#include<stdio.h>
int main(){
FILE *fp;
char ch;
//判断文件是否成功打开
if( (fp=fopen("D:\\demo.txt","wt+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
printf("Input a string:\n");
//每次从键盘读取一个字符并写入文件
while ( (ch=getchar()) != '\n' ){
fputc(ch,fp);
}
fclose(fp);
return 0;
}
4、按行输入: 函数:gets()、fgets()
函数原型: char *gets(char *s)
char *fgets(char *s,int size,FILE *stream)
函数参数:s:存放输入字符的缓冲区地址
size:输入的字符串长度
stream:输入文件流
函数返回值:成功:s
失败或读到文件尾:NULL
成功时返回s,到文件末尾或出错时返回NULL
gets不推荐使用,容易造成缓冲区溢出
与到 ‘\n’ 或已输入size-1个字符时返回,总是包含 '\0'
补充:
- gets()函数已经被淘汰,gets()的执行逻辑是寻找该输入流的’\n’并将’\n’作为输入结束符,但是若输入流数据超过存储空间大小的话会覆盖掉超出部分的内存数据,因此gets()函数十分容易造成缓冲区的溢出,不推荐使用。
- fgets()读取到’\n’或已经读取了size-1个字符后就会返回,并在整个读到的数据后面添加’\0’作为字符串结束符。
5、按行输出: 函数:puts()、fputs()
函数原型:int puts(const char *s)
int fputs(conse char *s,FILE *stream)
函数参数:s:存放输出字符的缓冲区地址
stream:输出文件流
成功时返回非负整数;出错时返回EOF
puts将缓冲区s中的字符串输出到stdout,并追加’\n'
fputs将缓冲区s中的字符串输出到stream,不追加"\n'
四、二进制数据读写(不仅可以读文本数据也可以写二进制数据)
1、函数:fread()
需要头文件:#include<stdio.h>
函数原型:size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream);
函数参数: ptr:存放读入数据的缓冲区
size:读取的每个数据项单位字节的大小
nmemb:读取的数据个数
stream:要读取的流
函数返回值:成功:实际读到的nmemb数目
失败:0
2、 函数:fwrite()
需要头文件:#include<stdio.h>
函数原型:size_t fwrite(void *ptr,size_t size,size_t nmemb,FILE *stream);
函数参数: ptr:存放写入数据的缓冲区
size:写入的每个数据项单位字节的大小
nmemb:写入的数据个数
stream:要写入的流
函数返回值:成功:实际写入的nmemb数目
失败:0
五、格式化输出:函数:sprintf()、snprintf()、fprintf()
函数原型:
int sprintf(char *str, const char *format, ...);
参数:
@ str:存储格式化后的字符串
@ format,...:可变参数,和printf写法一样。
返回值:
成功返回格式化的字符的个数,失败返回负值。
======================================================================
函数原型:
int snprintf(char *str, size_t size, const char *format, ...);
参数:
@ str:存储格式化后的字符串
@ size:输入的字符的个数,最后一个成员包含'\0'。
@ format,...:可变参数,和printf写法一样。
返回值:
成功返回格式化的字符的个数,失败返回负值,输出错误。
======================================================================
函数原型:
int fprintf(FILE *stream, const char *format, ...);
参数:
@ stream: 文件指针
@ format,...:可变参数,和printf写法
返回值:
成功返回格式化的字符的个数,失败返回负值,输出错误。
六、文件的定位:函数:fseek()、ftell()、rewind()
1、函数:fseek()
需要头文件:#include<stdio.h>
函数原型:int fseek(FILE *stream,long offset,int whence);
函数参数:stream:要定位的流
offset:相对于基准点whence的偏移量,
正数表示向前(向文件尾方向)移动,负数表示向后(向文件头方向)移动,0表示不移动
whence:基准点(取值见下)
函数返回值:成功:0,改变读写位置
失败:EOF,不改变读写位置
其中第三个参数whence的取值如下:
SEEK_SET:代表文件起始位置,数字表示为0
SEEK_CUR:代表文件当前的读写位置,数字表示为1
SEEK_END:代表文件结束位置,数字表示为2
使用fseek()函数可以定位流的读写位置,通过偏移量+基准值的计算将读写位置移动到指定位置,
其中第二个参数offset的值为正时表示向后移动,为负时表示向前移动,0表示不动。
2、函数:ftell()
需要头文件:#include<stdio.h>
函数原型:int ftell(FILE *stream);
函数参数:stream:要定位的流
函数返回值:成功:返回当前的读写位置
失败:EOF
3、函数:rewind
需要头文件:#include<stdio.h>
函数原型:void rewind(FILE *stream);
函数参数:stream:操作的流
函数返回值:无
rewind()函数会将当前读写位置返回至文件开头(rewind原意为“(磁带等)回滚,倒带”),其等价于
(void)fseek(stream, 0L, SEEK_SET)