华清远见25072班I/O学习day2
重点内容:
模块化读写(fread/fwrite)
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从stream指向的文件中读取nmemb项数据,每一项的大小为size 并且放入ptr指向的容器中
参数1:存放数据的容器,可以是任意类型的指针
参数2:每一项的大小
参数3:读取的总项数
参数4:文件指针
返回值:成功返回读取的项数,失败或者文件结束返回一个小于项数的值
注意:fread不区分到底是因为文件结束还是因为错误导致的结束,可以使用feof和ferror来判断
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:将ptr指向的容器中的内容,每项为size大小的nmemb项数据,写入到stream指向的文件中
参数1:要写入的数据起始地址
参数2:每一项的大小
参数3:要写入的项数
参数4:文件指针
返回值:成功返回写入的项数,失败或者文件结束返回一个小于项数的值
有关文件指针光标的操作(fseek/ftell/rewind)
int fseek(FILE *stream, long offset, int whence);
功能:将给定的文件指针的光标进行重新定位
参数1:文件指针
参数2:偏移量(以字节为单位) >0:表示从给定的基准向后偏移 =0:表示在基准处不偏移 <0:从基准处向前偏移
参数3:基准 SEEK_SET:文件开始位置 SEEK_CUR:光标当前位置 SEEK_END:文件结尾位置
返回值:成功返回0,失败返回-1并置位错误码
long ftell(FILE *stream);
功能:求出现在文件指针光标所在位置(以字节为单位)
参数:文件指针
返回值:光标目前所在位置
eg:求一个文件的大小 1、将光标先定位到结尾:fseek(fp, 0, SEEK_END); 2、此时使用ftell的返回值,就是文件的大小:ftell(fp);
void rewind(FILE *stream);
功能:将文件光标定位到开始位置 等同于:fseek(fp, 0, SEEK_SET);
参数:文件指针
返回值:无
有关缓冲区的问题
1.对于标准IO而言,分为三种缓冲区:
全缓存、行缓存、不缓存
2.全缓存:对应的是用户打开的文件指针:fp 其大小为4096字节
行缓存:对应的是stdin和stdout,其大小为1024字节
无缓冲:对应的是stderr,其大小为0字节
3.缓冲区的刷新时机
1、行缓存
1、遇到\n会刷新
2、当使用fflush刷新时,会刷新缓冲区
3、当程序结束时会刷新缓冲区
4、当使用exit退出进程时,会刷新标准io缓冲区
5、当输入输出发生切换时会刷新缓冲区
6、当缓冲区满了后,再向缓冲区中放数据时,会刷新该缓冲区
2、全缓存
1、当使用fflush刷新时,会刷新缓冲区
2、当程序结束时会刷新缓冲区
3、当使用exit退出进程时,会刷新标准io缓冲区
4、当输入输出发生切换时会刷新缓冲区
5、当缓冲区满了后,再向缓冲区中放数据时,会刷新该缓冲区
3、没有条件,放入立即刷新
有关错误码的问题
1.错误码是调用内核提供的函数时,如果调用出错,那么内核空间会向用户空间反馈一个错误信息
2.错误信息千奇百怪,并且是字符串内容,为了方便起见,给这些错误信息,进行编号
3.如果,内核空间函数调用出问题时,只需要反馈错误编号即可,这个错误编号就叫做错误码
4.常见错误码:可以通过指令 vi -t EIO 查看
文件IO:
文件IO是指用户直接调用内核提供的函数来进行IO操作,但是标准IO使用的是文件指针完成,文件IO使用的是文件描述符来实现
文件描述符
1.文件描述符是内核用来跟外部文件设备操作的一个非负整数
2.每一个文件描述符对应一个操作的文件
3.当一个程序启动后,系统默认打开三个特殊的文件描述符,分别是0号(标准输入stdin)、1号(标准输出)和2号(标准出错)
4.文件描述符的分配原则是:最小未使用原则
5.默认情况下,一个进程能够打开的文件描述符的个数为1024个,也就是0--1023
打开文件open
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
功能:使用系统调用,打开给定pathname对应的文件,并返回该文件对应的文件描述符
参数1:要被打开的文件
参数2:打开模式
1、必须包含以下三个中的一个
O_RDONLY: 只读的形式打开文件
O_WRONLY:只写的形式打开文件
O_RDWR:读写的形式打开文件
2、下面的可以包含也可以不包含,根据需要而定,所有标识通过位或连接
O_CREAT:如果文件不存在,则创建文件,注意,如果参数2中包含该标识, 则必须提供参数3,为文件的操作权限
O_TRUNC:清空文件内容
O_APPEND:追加形式
O_EXCL:确保创建新文件成功,如果文件已经存在,则报错,错误码为EEXIST
参数3:文件操作权限。注意:最终的文件权限,是由给定的权限跟默认掩码取反后取位与运算得到
运算:mode & (~umask) umak取决于终端
一般情况下:文件夹的权限最大为:0775 普通文件的权限:0664
返回值:成功返回打开的文件对应的文件描述符(最小未分配原则),失败返回-1并置位错误码
标准IO打开文件模式对应的操作位
关闭文件close
int close(int fd);
功能:关闭指定的文件描述符
参数:文件描述符
返回值:成功返回0,失败返回-1并置位错误码
数据读写(read、write)
ssize_t read(int fd, void *buf, size_t count);
功能:从fd文件描述符中读取count个字节的数据,并存放到buf指向的容器中
参数1:文件描述符
参数2:存放数据的容器起始地址
参数3:要读取数据的大小
返回值:成功返回读取的字节个数,失败返回-1并置位错误码
ssize_t write(int fd, const void *buf, size_t count);
功能:向fd文件描述符标识的文件中写入以buf作为起始地址的count个数据
参数1:文件描述符
参数2:要写入数据的容器起始地址
参数3:要写入数据的大小
返回值:成功返回写入的字节个数,失败返回-1并置位错误码
光标偏移(lseek)
off_t lseek(int fd, off_t offset, int whence);
功能:偏移fd文件描述符的光标
参数1:文件描述符
参数2:偏移量(以字节为单位)
>0:表示从给定的基准向后偏移
=0:表示在基准处不偏移
<0:从基准处向前偏移
参数3:基准
SEEK_SET:文件开始位置
SEEK_CUR:光标当前位置
SEEK_END:文件结尾位置
返回值:成功返回光标当前位置,失败返回(off_t)-1并置位错误码
作业:
1> 使用fread、fwrite实现两个文件的拷贝
程序源码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
FILE *fp=NULL;
FILE *fp1=NULL;
//从文件中读取数据
if((fp = fopen("./wcg.txt", "r")) == NULL)
{
printf("文件打开失败\n");
return -1;
}
if((fp1 = fopen("./wcgcope.txt", "w")) == NULL)
{
printf("文件打开失败\n");
return -1;
}
//将文件中的数据读取出来
char buf[128] = "";
while(1)
{
int res = fread(buf, 1, sizeof(buf), fp);
if(res < 128)
{
//判断是因为文件结束还是因为错误
if(feof(fp))
{
//printf("buf=%s\n", buf);
//将读取的数据全部写到新的文件中
fwrite(buf, res, 1, fp1);
break; //因为文件结束
}
if(ferror(fp))
{
printf("fread操作失败\n");
return -1;
}
}
//将读取的数据写到新的文件中
fwrite(buf, res, 1, fp1);
}
//关闭文件
fclose(fp);
fclose(fp1);
return 0;
}
2> 使用fread、fwrite实现将一个文件中的所有数字写入到另一个新文件中
程序源码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//定义函数用于筛选数字
void num(char *p,char *q,int len)
{
int j=0;
for(int i=0;i<len;i++)
{
if(p[i]>='0'&&p[i]<='9')
{
q[j++]=p[i];
}
}
q[j]='\0';
}
int main(int argc, const char *argv[])
{
FILE *fp=NULL;
FILE *fp1=NULL;
//从文件中读取数据
if((fp = fopen("./wcg.txt", "r")) == NULL)
{
printf("文件打开失败\n");
return -1;
}
if((fp1 = fopen("./wcgcope1.txt", "w")) == NULL)
{
printf("文件打开失败\n");
return -1;
}
//将文件中的数据读取出来
char buf[128] = "";
while(1)
{
char arr[128]="";
int res = fread(buf, 1, sizeof(buf), fp);
if(res < 128)
{
//判断是因为文件结束还是因为错误
if(feof(fp))
{
//将读取的数据全部写到新的文件中
num(buf,arr,res);
fwrite(arr,1,strlen(arr),fp1);
break; //因为文件结束
}
if(ferror(fp))
{
printf("fread操作失败\n");
return -1;
}
}
//将读取的数据写到新的文件中
num(buf,arr,res);
fwrite(arr,1,strlen(arr),fp1);
}
//关闭文件
fclose(fp);
fclose(fp1);
return 0;
}
3> 使用文件IO完成两个文件的拷贝
程序源码:
#include <25072head.h>
int main(int argc, const char *argv[])
{
//1、创建并打开一个文件
int fd = -1;
if((fd = open("./tt.txt", O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1)
{
perror("open error");
return -1;
}
int fd1=-1;
if((fd1 = open("./ttcope.txt", O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1)
{
perror("open error");
return -1;
}printf("fd = %d\n", fd);
//向文件中写入数据
char buf[128] = "";
while(1)
{
//从终端获取数据
int res = read(0, buf, sizeof(buf));
if(res == 1)
{
break;
}
//将数据写入到fd指向的文件中
write(fd, buf, res);
}
//从文件中读取数据
lseek(fd,0,SEEK_SET);
char rbuf[128] = "";
int res = read(fd, rbuf, sizeof(rbuf)); //从文件中读数据
//将数据写到终端上
write(fd1, rbuf, res);
//关闭文件
close(fd);
return 0;
}
4> 自己总结标准IO和文件IO的区别与联系
标准IO:代码和程序员之间的数据交互
文件IO:代码和文件系统之间的数据交互
1.文件IO没有缓冲区,标准IO函数存在缓冲区
2.标准IO函数的读取速度快于文件IO函数
3,标准IO函数本质上就是文件IO的二次封装,标准IO=文件IO+缓冲区
4.文件IO可以直接和内核交互,标准IO不可以直接和内核交互,标准IO函数只可以访问普通文件,但是文件IO函数可以访问任意的特殊文件(管道,套接字)
5.文件IO属于系统文件,依赖于操作系统,可以移植性差
6.标准IO函数属于库文件,可以在不同的操作系统中使用,可移植性强
7.文件IO遵循POIX标准,标准IO函数满足ANSI标准
8.文件IO函数操作文件需要借助于文件描述符
9.标准IO函数操作文件需要借助于流指针
5> 思维导图
6> 牛客网刷题 >= 26