C++中的标准IO和文件IO的简述
作者:zanzh520
一.标准IO和文件IO的区别
标准IO:
使用系统提供的库函数实现
文件IO:
基于系统调用完成,每进行一次系统调用,进程会从用户空间向内核空间进行一次切换,当用户空间与内核空间进行切换时,进程就会进入一次挂起状态,从而导致进程执行效率低
文件IO与标准IO的区别:
标准IO相比于文件IO而言,提供了缓冲区,用户可以将数据先放入缓冲区中,等到缓冲区时机到了后,统一进行一次系统调用,将数据刷入内核空间
标准IO接口:
printf/scanf、fopen/fclose、fgetc/fputc、fgets/futs、fprintf/fscaf、
fread/fwrite、fseek/ftell/rewind
文件IO接口:
open/close、read/write、lseek
二.标准IO
1.缓冲区
缓冲区就是统一一起处理

左边的图:
系统分配0-4G的虚拟内存
用户空间0-3G
内核空间3-4G
右边的图:
两种执行方式,第一种通过库缓冲执行,第二种直接调用到内核空间
2.sizeof和strlen的不同用法
1. `sizeof(arr)`:数组**最多能装多少字节**,编译就定死,不管里面有没有文字;
2. `strlen(arr)`:数组**现在实际装了几个可见字符**,运行时扫描,必须带`\0`才能正常工作。
3.关于头文件如何通用的做法(""到<>的应用)
Linux头文件统一放一起
“...”作用是当前文件夹下的...头文件
为了更方便,应用<...>,但有一个前提是要把...这个文件放在/usr/include/目录下
这个目录下的东西包含常用头文件

4.FILE结构体的原型

特殊的FILE指针
这三个指针,全部都是针对于终端文件而言的,当程序启动后,系统默认打开的三个特殊文件指针
stderr: 标准出错指针
stdin: 标准输入指针
stdout: 标准输出指针
5.错误码
概念:
当内核提供的函数出错后,内核空间会向用户空间反馈一个错误信息,由于错误信息比较多也比较复杂,系统就给每种不同的错误信息起了一个编号,用一个整数表示,这个整数就是错误码
错误码都是大于或等于0的数字:

关于错误码的处理函数(strerror、perror)
常用perror()

6.单字符读写:fputc/fgetc
int fgetc(FILE *stream);
功能:从指定文件中读取一个字符数据,并以无符号整数的形式返回
参数:文件指针
返回值:成功返回读取的字符对应的无符号整数,失败返回EOF并置位错误码
int fputc(int c, FILE *stream);
功能:将指定的字符c写入到stream指向的文件中
参数1:要写入的字符对应的无符号整数
参数2:文件指针
返回值:成功返回写入字符对的无符号整数,失败返回EOF并置位错误码
7.字符串读写:fgets/fputs
int fputs(const char *s, FILE *stream);
功能:将指定的字符串,写入到指定的文件中
参数1:要被写入的字符串
参数2:文件指针
返回值:成功返回本次写入字符的个数,失败返回EOF
char *fgets(char *s, int size, FILE *stream);
功能:从stream指向的文件中最多读取size-1个字符到s容器中,遇到回车或文件结束,会结束一次读取,并且会将回车放入容器,最后自动加上一个字符串结束标识'\0'
参数1:字符数组容器起始地址
参数2:要读取的字符个数,最多读取size-1个字符
参数3:文件指针
返回值:成功返回容器s的起始地址,失败返回NULL
8. 关于标准IO的缓冲区问题(缓冲区分为三种)
行缓存:
和终端文件相关的缓冲区叫做行缓存,行缓冲区的大小为1024字节
对应的文件指针:stdin、stdout
全缓存:
和外界文件相关的缓冲区叫做全缓存,全缓冲区的大小为4096字节
对应的文件指针:fp
不缓存:
和标准出错相关的缓冲区叫不缓存,不缓存的大小为0字节
对应的文件指针:stderr
注意:在未使用时都是0,使用后才是对应的字节数
9.缓冲区的刷新时机
9.1 缓冲区刷新函数:fflush
int fflush(FILE *stream);
功能:刷新给定的文件指针对应的缓冲区
参数:文件指针
返回值:成功返回0,失败返回EOF并置位错误码
9.2 会刷新缓冲区的7种方法
1+3+3记忆法
1种是没到刷新时机不刷新
3种是程序结束,换行,输入输出切换
3种是关闭文件指针,刷新文件指针,缓存区满了
行缓存和全缓存只有换行是不同的(全缓存的换行不刷新)
不缓存只要放入数据,立马刷新

10. 格式化读写:fprintf/fscanf
int fprintf(FILE *stream, const char *format, ...);
功能:向指定的文件中输出一个格式串
参数1:文件指针
参数2:格式串,可以包含格式控制符,%d(整数)、%s(字符串)、%f(小数)、%lf(小数)
参数3:可变参数,输出项列表,参数个数由参数2中的格式控制符的个数决定
返回值:成功返回输出的字符个数,失败返回一个负数
int fscanf(FILE *stream, const char *format, ...);
功能:从指定的文件中以指定的格式读取数据,放入程序中
参数1:文件指针
参数2:格式串,可以包含格式控制符,%d(整数)、%s(字符串)、%f(小数)、%lf(小数)
参数3:可变参数,输入项地址列表,参数个数由参数2中的格式控制符的个数决定
返回值:成功返回读入的项数,失败返回EOF并置位错误码
举例:
//向标准输出文件指针中写入数据
fprintf(stdout, "%d %lf %s\n", 520, 3.14, "I Love Xingqiu");
fscanf(fp, "%s %d", usrName, &pwd); //从外部文件中分别读取一个字符串和一个整数放入程序中的
11.格式串转字符串存入字符数组中:sprintf/snprintf
1> 目前所学的函数中,printf是向终端打印一个格式串,fprintf是向外部文件中打印一个格式串
2> 有时候,想要将多个不同数据类型的数据,组成一个字符串放入字符数组中,此时我们就可以使用
sprintf或snprintf
int sprintf(char *str, const char *format, ...);
功能:将指定的格式串转换为字符串,放入字符数组中
参数1:字符数组的起始地址
参数2:格式串,可以包含多个格式控制符
参数3:可变参数
返回值:成功返回转换的字符个数,失败返回EOF
对于上述函数而言,用一个小的容器去存储一个大的转换后的字符时,会出现指针越界的段错误,为了安全起见,引入了snprintf
int snprintf(char *str, size_t size, const char *format, ...);
功能:将格式串中最多size-1个字符转换为字符串,存放到字符数组中
参数1:字符数组的起始地址
参数2:要转换的字符个数,最多为size-1
参数3:格式串,可以包含多个格式控制符
参数4:可变参数
返回值:如果转换的字符小于size时,返回值就是成功转换字符的个数,如果大于size则只转换
size-1个字符,返回值就是size,失败返回EOF
例如:
//需求:将多种数据类型的数据连接成一个字符串,放入字符数组中
snprintf(buf, "%d %s %lf", 1001, "zhangsanfeng", 99.5);
12.模块化读写 (fread/fwrite)
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从stream指向的文件中,读取nmemb项数据,每一项的大小为size,将整个结果放入ptr指向的容器中
参数1:容器指针,是一个void*类型,表示可以存储任意类型的数据
参数2:要读取数据每一项的大小
参数3:要读取数据的项数
参数4:文件指针
返回值:成功返回nmemb,就是成功读取的项数,失败返回小于项数的值,或者是0
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:向stream指向的文件中写入nmemb项数据,每一项的大小为size,数据的起始地址为ptr
参数1:要写入数据的起始地址
参数2:每一项的大小
参数3:要写入的总项数
参数4:文件指针
返回值:成功返回写入的项数,失败返回小于项数的值,或者是0
举例:
fwrite(wbuf, strlen(wbuf), 1, fp); //将字符串以每个字符串为单位写入文件,
每次写一项
int res = fread(rbuf, 1, sizeof(rbuf), fp); //从文件中读取一个字符串
fwrite(rbuf, 1, res, stdout); //向标准输出中写入数据
注意:通常在fread后使用fwrite(read和write同理)
13. 关于文件光标:fseek/ftell/rewind
int fseek(FILE *stream, long offset, int whence);
功能:移动文件光标位置,将光标从指定位置处进行前后偏移
参数1:文件指针
参数2:偏移量
>0:表示从指定位置向后偏移n个字节
<0:表示从指定位置向前偏移n个字节
=0:在指定位置处不偏移
参数3:偏移的起始位置
SEEK_SET:文件起始位置
SEEK_CUR:文件指针当前位置
SEEK_END:文件结束位置
返回值:成功返回0,失败返回-1并置位错误码
long ftell(FILE *stream);
功能:获取文件指针当前的偏移量
参数:文件指针
返回值:成功返回文件指针所在的位置,失败返回-1并置位错误码
例如: fseek(fp, 0, SEEK_END); //将光标定位在结尾
ftell(fp); //该函数的返回值,就是文件大小
void rewind(FILE *stream);
功能:将文件光标定位在开头:fseek(fp, 0, SEEK_SET);
参数:文件指针
返回值:无
三.文件IO
int open(const char *pathname, int flags, mode_t mode);
参数说明:其中参数3是可变的(可添加可不添加)
参数1:文件路径
参数2:打开模式

参数3:如果参数2中的flag中有O_CREAT时,表示创建新文件参数3就必须给定,表示新创建的文件的权限
参数3讲解:参数3来源于权限r(4),w(2),x(1)的加和,如果和~umask按位与(&)时候,需要变成二进制形式,其中umask是8进制形式,也需要变成二进制形式
所以为了更方便,通常在open()函数之前引用函数umask(0000)设置,方便后续权限设置

mode&(~umask)的计算
0777&(~0022)结果0755

关于参数的几个说明
(1)如果当前参数给定了创建的文件权限,最终的结果也不一定是参数3的值,系统会用你给定的参数3的值,与系统的umask取反的值进行位与运算后,才是最终创建文件的权限(mode & ~umask)
(2)当前终端的umask的值,可以通过指令 umask 来查看,一般默认为为 0022,表示当前进程所在的组中对该文件没有写权限,其他用户对该文件也没有写权限
(3)当前终端的umask的值是可以更改的,通过指令:umask 数字进行更改,这种方式只对当前终端有效
(4)普通文件的权限一般为:0644,表示当前用户没有可执行权限,当前组中其他用户和其他组中的用户都只有读权限
(5)目录文件的权限一般为:0755,表示当前用户具有可读、可写、可执行,当前组中其他用户和其他组中的用户都没有可写权限
(6)注意:如果不给权限,那么当前创建的权限会是一个随机值
文件描述符
1> 文件描述符的本质是一个大于等于0的整数,在使用open函数打开文件时,就会产生一个用于操作
文件的句柄,这就是文件描述符
2> 在一个进程中,能够打开的文件描述符是有限制的,一般是1024个,[0,1023],可以通过指令 ulimit -a进行查看,如果要更改这个限制,可以通过指令 ulimit -n 数字,进行更改
3> 文件描述符的使用原则一般是最小未分配原则
4> 特殊的文件描述符:0、1、2,这三个文件描述符在一个进程启动时就默认被打开了,分别表示标
准输入、标准输出、标准错误

ssize_t read(int fd, void *buf, size_t count);
功能:从fd文件描述符引用的文件中读取count的字符放入buf对应的容器中
参数1:已经打开文件对应的文件描述符
参数2:容器的起始地址
参数3:要读取的字符个数
返回值:成功返回读取的字符个数,这个个数可能会小于count的值,失败返回-1并置位错误码
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:将buf容器中的count个数据,写入到fd引用的文件中
参数1:已经打开的文件的文件描述符
参数2:要写入的数据的起始地址
参数3:要写入数据的个数
返回值:成功返回写入的字符个数,这个个数可能小于count,失败返回-1并置位错误码
off_t lseek(int fd, off_t offset, int whence);
功能:移动光标的位置,并返回光标现在所在的位置
参数1:文件描述符
参数2:偏移量
>0:表示从指定位置向后偏移n个字节
<0:表示从指定位置向前偏移n个字节
=0:在指定位置处不偏移
参数3:偏移的起始位置
SEEK_SET:文件起始位置
SEEK_CUR:文件指针当前位置
SEEK_END:文件结束位置
返回值:光标现在所在的位置
注意: lseek = fseek+ftell
到此这篇关于C++中的标准IO和文件IO的简述的文章就介绍到这了,更多相关C++ 标准IO和文件IO内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
