- 一、认识IO
- 1.IO的种类
- 2.缓冲区
- 2.1缓冲区的种类
- 2.2缓冲区的刷新机制
- 2.2.1行缓冲区的刷新机制
- 2.2.2全缓冲区的刷新机制
- 3.常用函数接口种类
- 二、标准IO
- 1.什么是FILE
- 2.什么是错误码
- 3.fopen / fclose
- 3.1fopen
- 3.2fclose
- 4.fgetc / fputc
- 4.1fgetc(getc,getchar)
- 4.2fputc(putc,putchar)
- 5.fgets / fputs
- 5.1fgets(gets)
- 5.2fputs(puts)
- 6.fread / fwrite
- 6.1fread
- 6.2fwrite
- 7.格式化控制相关函数
- 7.1sprintf
- 7.2snprintf
- 7.3fprintf
- 8.关于光标位置的相关函数
- 8.1fseek
- 8.2ftell
- 8.3rewind
- 三、文件IO
- 1.什么是文件描述符
- 2.open
- 3.close
一、认识IO 1.IO的种类
| 标准IO | 文件IO |
|---|---|
| ANSI C (标准C库) | posix (可移植操作系统的标准) |
| 库函数 | 系统调用 |
系统调用:从用户空间到内核空间的切换过程,不同的操作系统系统调用的接口是不同的。只要从用户空间到内核空间了就要发生一次系统调用,效率比较低。系统调用没有缓冲区。
库函数:库函数的平台的通用新更强,库函数有缓冲区,效率比系统调用高。库函数=缓冲区+系统调用
全缓存:和文件相关的缓冲区就是全缓存 (fp 4096字节)
行缓存:和终端相关的缓冲区是行缓存 (stdin stdout 1024字节)
不缓存:标准出错没有缓冲区(stderr 0字节)
2.2缓冲区的刷新机制 2.2.1行缓冲区的刷新机制1.行缓存遇到’n’字符会刷新缓冲区
2.程序执行结束,也会刷新行缓冲区
3.当输入和输出发生切换的时候也会刷新缓冲区
4.当关闭文件指针的时候也会刷新缓冲区
5.fflush主动刷新缓冲区
6.如果(行)缓冲区满了,会刷新缓冲区
1.程序执行结束,也会刷新全缓存
2.输入输出发生切换的时候,会刷新全缓存
3.关闭文件指针,会刷新全缓存
4.fflush主动刷新缓冲区
5.全缓存满,也会刷新缓冲区
标准IO:printf scanf fopen fclose fread fwrite fgetc fputc fgets fputs…
文件IO:open read write close…
二、标准IO 1.什么是FILE标准IO库的操作依赖于流(stream)进行。打开一个文件就会创建一个FILE对象,返回一个FILE**的指针(文件指针)此对象是一个结构体。在这个结构体中记录所有的关于文件的信息,以后对文件的操作通过FILE*来完成。
/usr/include/
ctags -R //创建索引
vi -t FILE //查找FILE实现的位置
typedef struct _IO_FILE FILE;
struct _IO_FILE {
char* _IO_buf_base; //缓冲区的首地址
char* _IO_buf_end; //缓冲区的结束地址
...
};
在一个正在执行的程序中,已经创建了三个FILE的指针,如下:
stdin: 标准输入 (scanf)
stdout 标准输出 (printf)
stderr: 标准出错
在文件IO或标准IO相关接口被调用的时候,如果出错了,操作系统会给应用程序返回错误码
#includechar* strerror(int errnum); 功能:根据错误码转换错误信息 参数: @errnum:这个是错误码,包含 #include extern int errno; 返回值:错误信息字符串 void perror(const char *s); 功能:打印错误码对应的信息 参数: @s:用户附加的信息 返回值:无
strerror的使用
#include#include #include //extern int errno; int main(int argc,const char * argv[]) { //定义文件指针 FILE *fp; //以只读的方式打开文件,文件不存在会报错 fp = fopen("./hello.txt","r"); if(fp == NULL){ printf("%sn",strerror(errno)); return -1; } fclose(fp); return 0; }
perror的使用
#include3.fopen / fclose 3.1fopenint main(int argc,const char * argv[]) { //定义文件指针 FILE *fp; //以只读的方式打开文件,文件不存在会报错 fp = fopen("./hello.txt","r"); if(fp == NULL){ perror("open hello.txt"); return -1; } fclose(fp); return 0; }
功能:使用标准IO打开文件
#includeFILE* fopen(const char *pathname, const char *mode);
参数:
@pathname: 路径/文件名 “./hello.txt”
@mode:打开文件的方式 “r”
r:以只读的方式打开文件,将文件中的光标定位在文件的开头
r+:以读写的方式打开文件,将文件中的光标定位在文件的开头
w:以只写的方式打开文件,如果文件不存在就创建文件,如果文件存在就清空,将光标定位在开头
w+:以读写的方式打开文件,如果文件不存在就创建文件,如果文件存在就清空,将光标定位在开头
a:以追加(结尾写)的方式打开文件,如果文件不存在就创建文件,将光标定位在文件的结尾
a+:以读和追加(结尾写)的方式打开文件,如果文件不存在就创建文件,如果读从开头读,写在文件的结尾写
返回值:成功返回文件指针,失败返回NULL,置位错误码
实例:
#include3.2fcloseint main(int argc,const char * argv[]) { //定义文件指针 FILE *fp; //以追加的方式打开文件,如果文件存在,不会清空(追加) if(NULL == (fp = fopen("./hello.txt","a"))) { printf("create file errorn"); return -1; } return 0; }
功能:关闭文件
int fclose(FILE* stream);
参数:
@stream:文件指针
返回值:成功返回0,失败返回EOF(end of file),并置位错误码
实例:
#include4.fgetc / fputc 4.1fgetc(getc,getchar)int main(int argc,const char * argv[]) { FILE *fp; //以只写的方式打开文件 fp = fopen("./hello.txt","w"); if(fp == NULL){ printf("create file errorn"); return -1; } //关闭文件 if(fclose(fp)<0){ printf("close file errorn"); return -1; } return 0; }
功能:从文件中读取一个字符
int fgetc(FILE *stream);
参数:
@stream:文件指针
返回值:成功返回字符的ascii,读取文件的结尾EOF,失败返回error
实例:
#include4.2fputc(putc,putchar)int main(int argc,const char * argv[]) { FILE *fp; fp = fopen("./01fopen.c","r"); if(fp == NULL){ perror("fopen error"); return -1; } int i=0; while(i++<30){ printf("%c",fgetc(fp)); } puts("");//换行 fclose(fp); return 0; }
功能:向文件中写一个字符
int fputc(int c, FILE *stream);
参数:
@c:被写字符的ascii
@stream:文件指针
返回值:成功返回ascii,失败返回EOF
实例:
#include5.fgets / fputs 5.1fgets(gets)int main(int argc, const char *argv[]) { FILE *fp; fp = fopen("./test.txt", "w"); if (fp == NULL) { perror("fopen error"); return -1; } fputc('h', fp); fputc('e', fp); fputc('l', fp); fputc('l', fp); fputc('o', fp); fclose(fp); return 0; }
功能:从文件指针中读取一个字符串
char *fgets(char *s, int size, FILE *stream);
参数:
@s:用来存储读取到字符的首地址
@size:读取的大小(最多读取size-1)
@stream:文件指针
返回值:成功返回s,失败返回NULL
注:遇到换行或者EOF的停止读取,换行符也会被读取到s的缓冲区中,并且在换行符之后存储一个' '结束符
实例:
#include5.2fputs(puts)#include int main(int argc,const char * argv[]) { char buffer[1024]; printf("input string > "); fgets(buffer,sizeof(buffer),stdin); //将读取到的换行符设置为' ' //hellon //buffer=hello'n'' ' //strlen(buffer)=6 //buffer[5]=' ' buffer[strlen(buffer)-1]=' '; printf("%sn",buffer); return 0; }
功能:将s字符串中的内容写入到文件中
int fputs(const char *s, FILE *stream);
参数:
@s:首地址
@stream:文件指针
返回值:成功返回写入的字符的个数,失败返回EOF
实例:
#include6.fread / fwrite 6.1freadint main(int argc,const char * argv[]) { FILE*fp; //向标准输出中写入字符串 fputs("helloworld",stdout); //向标准出错中写入字符串 fputs("helloworld",stderr); if ((fp = fopen("hello.txt", "w")) == NULL) { perror("fopen file error"); return -1; } fputs("i love china",fp); fclose(fp); return 0; }
功能:从文件中读取数据到ptr中
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数:
@ptr:保存读取到数据的首地址
@size:每一项的大小
@nmemb:项的个数
@stream:文件指针
返回值:成功返回读取到的项目的个数,如果是失败或者读取到的文件的结尾返回值是要小于nnemb或者0
错误还是读取到文件的结尾需要通过feof(fp)或者ferror(fp)来判断
实例:
#include6.2fwrite#define PRINT_ERR(msg) do{perror(msg);return -1;}while(0) int main(int argc,const char * argv[]) { FILE *fp; int num; if((fp = fopen("test.txt","r"))==NULL) PRINT_ERR("fopen error"); fread(&num,4,1,fp); printf("read num = %dn",num); fclose(fp); return 0; }
功能:将ptr中的数据写入到文件中
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
参数:
@ptr:数据的首地址
@size:每一项的大小
@nmemb:项的个数
@stream:文件指针
返回值:成功返回写的项目的个数,如果是失败返回值是要小于nnemb或者0
实例:
#include7.格式化控制相关函数 7.1sprintf#define PRINT_ERR(msg) do{perror(msg);return -1;}while(0) int main(int argc,const char * argv[]) { FILE *fp; int num; if((fp = fopen("test.txt","r"))==NULL) PRINT_ERR("fopen error"); fread(&num,4,1,fp); printf("read num = %dn",num); fclose(fp); return 0; }
功能:向str中进行字符串的格式化
int sprintf(char *str, const char *format, ...);
参数:
@str:首地址
@format:控制格式
返回值:成功返回成功格式化字符的个数,失败返回负数
char s[50];
sprintf(s,"%4d-%02d-%02d %02d:%02d:%02dn",tm->tm_year+1900,
tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
//把双引号中的字符串存放在s的数组中
//s="2022-4-26 17:30:20"
fputs(s,fp); //将s中的字符串写入到文件中
注:当使用sprintf往数组中写入字符的时候,如果出现越界,虽然编译的时候会提示警告,但是在程序运行的时候有多少字符就格式化多少字符,这个内存越界的错误仍然会出现
7.2snprintf功能:将字符串格式化到str的数组中
int snprintf(char *str, size_t size, const char *format, ...);
参数:
@str:存放格式化后的字符串的内存首地址
@size:大小(字符size-1)
@format:格式化的控制格式
返回值:成功返回字符的个数,失败返回负数
#include7.3fprintfint main(int argc,const char * argv[]) { char s[10]; snprintf(s,sizeof(s),"%s","helloworld1234"); printf("s = %sn",s); //上输入的字符个数大于s的大小的时候,最多格式化sizeof(s)-1个字符,在s最后一个字符的 //位置补上' ' return 0; }
功能:将格式化后的字符串向对应的文件指针中输出
int fprintf(FILE *stream, const char *format, ...);
参数:
@stream:文件指针
@format:控制格式
返回值:成功返回**>0**,失败返回**<0**
功能:修改光标的位置
int fseek(FILE *stream, long offset, int whence);
参数:
@stream:文件指针
@offset:偏移
>0 向后偏移 =0 不偏移 <0 向前偏移
@whence:
SEEK_SET,开头 SEEK_CUR,当前位置 SEEK_END,结尾
返回值:成功返回0,失败返回 -1 ,置位错误码
实例:
fseek(fp,0,SEEK_SET);将光标定位在文件的开头 fseek(fp,10,SEEK_SET);从文件的开头向后偏移10个字节 fseek(fp,10,SEEK_CUR);从光标当前位置向后偏移10个字节 fseek(fp,-10,SEEK_END);从文件结尾向前偏移10个字节 fseek(fp,0,SEEK_END);将光标定位在文件结尾8.2ftell
功能:获取光标当前的位置
long ftell(FILE *stream)
参数:
@stream:文件指针
返回值:成功返回光标的位置,失败返回 -1 ,置位错误码
实例:
fseek(fp,0,SEEK_END); ftell(fp); ===>功能统计文件所占用的字符的个数(大小)8.3rewind
功能:恢复光标的位置到文件的开头
void rewind(FILE *stream);
参数:
@stream:文件指针
返回值:无
实例:
rewind(fp) == fseek(fp,0,SEEK_SET) 等价三、文件IO
文件IO:是通过系统调用实现的,只要调用文件IO的函数接口就会从用户空间进入到内核空间,系统调用的效率比较低,因为没有缓冲区的概念。
文件IO常见的接口有open,read,write,close,lseek等
文件描述符:文件描述符本身是一个大于等于0的整数,使用open打开文件之后返回文件描述符,以后在操作文件的时候通过这个文件描述符完成。在一个正在执行的程序中文件描述符的范围是0-1023(1024个),可以通过ulimit -a查看限制,这个限制值是可以修改的(ulimit -n 2048)。文件描述符的分配原则是最小未使用。在一个正在执行的程序中默认已经有三个文件描述符了。
0:标准输入文件描述符
1:标准输出文件描述符
2:标准错误文件描述符
ulimit -a real-time non-blocking time (microseconds, -R) unlimited core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 15294 max locked memory (kbytes, -l) 498056 max memory size (kbytes, -m) unlimited open files (-n) 1048576 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 15294 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
注:在标准IO的文件指针结构体中其实默认包含文件描述符_fileno
#include2.openint main(int argc,const char * argv[]) { printf("%dn",stdin->_fileno); //标准输入文件描述符 0 printf("%dn",stdout->_fileno); //标准输出文件描述符 1 printf("%dn",stderr->_fileno); //标准出错文件描述符 2 return 0; }
功能:打开文件
#include#include #include int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
参数:
@pathname:文件的路径和文件的名字
@flags:文件的打开方式
@mode:只有在第二个flags参数写O_CREAT的时候才需要填充,第三个参数代表创建文件的权限
O_RDONLY, 只读的方式打开文件
O_WRONLY, 只写的方式打开文件
O_RDWR. 读写的方式打开文件
O_APPEND 以追加的方式打开文件
O_CREAT 创建文件,必须填写第三个参数,第三个参数代表创建这个文件的权限
O_TRUNC 清空文件
O_EXCL 确保文件存在,跟O_CREAT结合使用,如果文件存在就返回EEXIST已存在错误,
如果文件不存在正常创建
实际创建文件权限:mode & (~umask) umask的值0002 umask 0000
~umask=0664 ~umask=0666
文件的最大权限是0666 mode & 0664 mode & 0666
返回值:成功返回文件描述符,失败返回 -1 置位错误码
| 标准IO | 文件IO | 含义 |
|---|---|---|
| “r” | O_RDONLY | 只读的方式打开文件 |
| “r+” | O_RDWR | 以读写的方式打开文件 |
| “w” | O_WRONLY|O_CREAT|O_TRUNC, 0664 | 以只写的方式打开文件,如果文件存在就清空,如果文件不存在就创建 |
| “w+” | O_RDWR|O_CREAT|O_TRUNC,0664 | 以读写的方式打开文件,如果文件存在就清空,如果文件不存在就创建 |
| “a” | O_WRONLY|O_APPEND|O_CREAT,0664 | 以追加(结尾写)的方式打开文件,如果文件不存在就创建文件 |
| “a+” | O_RDWR|O_APPEND|O_CREAT,0664 | 以追加(结尾写,开头读)的方式打开文件,如果文件不存在就创建文件 |
#include3.close#include #include #include #include #include #include #define PRINT_ERR(msg) do{ perror(msg); return -1; }while(0) int main(int argc, const char *argv[]) { int fd; //读写的方式打开文件,如果文件不存在就创建文件 //如果文件存在就报文件已存在的错误EEXIST fd = open("hello.c", O_RDWR | O_CREAT | O_EXCL, 0666); if (fd == -1) { if (errno == EEXIST) { //文件已经存在,不需要创建,直接打开 printf("文件已经存在,不需要创建,直接打开n"); fd = open("hello.c", O_RDWR, 0666); } else { PRINT_ERR("open error"); } } return 0; }
功能:关闭文件
#includeint close(int fd);
参数:
@fd:文件描述符
返回值:成功返回0,失败返回 -1置位错误码
#includeint main(int argc, const char *argv[]) { int fd; //读写的方式打开文件,如果文件不存在就创建文件 //如果文件存在就报文件已存在的错误EEXIST fd = open("hello.c", O_RDWR | O_CREAT | O_EXCL, 0666); if (fd == -1) { if (errno == EEXIST) { //文件已经存在,不需要创建,直接打开 printf("文件已经存在,不需要创建,直接打开n"); fd = open("hello.c", O_RDWR, 0666); } else { PRINT_ERR("open error"); } } printf("fd = %dn",fd); //这里的fd是3 //每一个正在执行的程序都有0-1023这1024个文件描述符 //关闭这个程序中的文件描述符,不影响其他程序的文件描述符。 close(fd); return 0; }



