文章目录
c语言文件函数
写函数:
c
FILE* fp=fopen("myfile","w");
if(!fp)
{
perror("fopen");
}
const char* msg="hello jib\n";
int cnt=5;
while(cnt--)
{
fwrite(msg,strlen(msg),1,fp);
}
fclose(fp);
读函数:
c
fp=fopen("myfile","r");
char buf[1024];
size_t s=fread(buf,1,sizeof(buf)-1,fp);
fclose(fp);
详细的C语言文件知识点请看我这篇文章,这里不过多解释了
C语言文件操作知识
🚩系统接口
读写函数:
c
umask(0);
int fd=open("myfile",O_RDWR|O_CREAT,0644);
if(fd<0)
{
perror("open");
return -1;
}
char* msg="hello world\n";
int cnt=5;
int len=strlen(msg);
while(cnt--)
{
write(fd,msg,len);
}
lseek(fd,0,SEEK_SET);
char buf[1024];
while(1)
{
size_t s=read(fd,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]='\0';
printf("%s\n",buf);
}
else{
break;
}
}

lseek定位文件,写入文件之后指针到了文件末尾,
off_t lseek(int fd, off_t offset, int whence);
fd:文件描述符
offset:偏移量
whence:基准位置 分为:SEEK_SET SEEK_CUR SEEK_END
这里是定位文件开头
🚩open
打开一个文件,
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
🚩参数细节:
pathname 要创建或打开的文件名字
flags:打开文件时,可以传入多个参数选项,用下列一个或多个常量构成flags
- O_RDONLY 只读打开
- O_WRONLY 只写打开
- O_RDWR 既可以读可以写
以上三种模式只能选一种
- O_CREAT
若是要打开文件不存在,则创建这个文件
- O_APPEND
追加写,(下文重定向会讲)
返回值:
成功了返回 文件描述符
失败了返回 -1
文件标识符
通过open,我们知道文件标识符就是一个整数
c
int fd=open("myfile",O_RDWR|O_CREAT,0644);
if(fd<0)
{
perror("open");
return -1;
}
cout<<fd<<endl;

系统分配文件标识符是从0开始的,为什么我新开的文件标识符是3呢?
因为系统自动打开了3个文件:
键盘,显示器,显示器 ,
分别是标准输入,标准输出,标准错误对应的文件标识符 0,1,2
所以输入输出还可以这样,
c
char buf[1024];
size_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]='\0';
write(1,buf,strlen(buf));
write(2,buf,strlen(buf));
}


PCB中有files指针,指向一张表files_struct,表中有file数组,数组中每个元素都是指向已打开文件的下标。
所以文件标识符本质就是该数组的下标,只要有这个下标就能找到对应的文件
分配文件标识符
我们关闭0文件试试
c
close(0);
//close(2);
size_t fd=open("myfile",O_RDWR|O_CREAT);
if(fd<0)
{
perror("open");
}
else{
cout<<fd;
}

结果系统把0文件标识符给了myfile
说明分配规则是从struct_file寻找到下标最小且未被使用的文件标识符
🚩重定向
如果我们关闭1呢
c
close(1);
size_t fd=open("myfile",O_RDWR|O_CREAT);
if(fd<0)
{
perror("open");
return 1;
}
else{
cout<<fd;
}

运行发现无结果,为什么呢?
原本要向显示器打印的,但是显示器被关了,标识符1被myfile占了,但是系统依旧向1中输入,所以最终结果给了文件myfile

我们看到第一个hello的h被覆盖为1了,因为我之前向myfile中已经输入了许多hello world
要想清空文件,只需改变打开的方式
size_t fd=open("myfile",O_RDWR|O_CREAT|O_TRUNC);

或者追加模式

常见的重定向:>,>>,<
> 把内容输出到文件中
>>就是追加模式
< 把内容输入到指令中
比如cat < text.txt 但我们一般省略 <
小总
C语言的I/O函数其实是封装了系统函数,打开权限a就是封装了追加模式的系统函数,各种语言都有自己文件操作函数,但是本质都是封装了系统函数open,write等等
而且必定封装了fd,所有语言级别的文件操作函数都是通过fd完成的,
🚩缓冲区
先来看代码:
c
char* str="hello write\n";
char* fstr="hello fwrite\n";
cout<<"cout"<<endl;
fprintf(stdout,"hello fprintf\n");
fwrite(fstr,strlen(fstr),1,stdout);
write(1,str,strlen(str));

正常输出,如果加入fork()会怎样

正常运行还是没问题,问题来了,当我们向文件输入,我们发现,C语言的文件函数打印了两次,系统函数只打印了一次。
为啥会这样呢??因为有缓冲区
C语言有自己的缓冲区
🚩 缓冲区刷新模式
- 无缓冲 - - - 直接刷新
- 行缓冲 - - - 不刷新,遇到\n刷新
- 全缓冲 - - - 直到缓冲区满了才刷新
进程退出的时候也会刷新缓冲区
显示器刷新正常是行刷新,遇到\n就刷新,但当向文件中刷新时,显示器就变成了全缓冲,即遇到\n不会立刻给你刷新,放入缓冲区里了
但是为什么C语言文件函数打印了两遍呢?
因为fork创建了子进程,子进程会复制当前的缓冲区,相当于C语言当前缓冲区有了两份,刷新了两遍缓冲区
为什么系统函数只打印了1遍呢 ?
因为操作系统内核有自己的缓冲区,也就是系统函数只在系统缓冲区一份,
实际上,C语言的缓冲区要刷新到操作系统内核,然后再刷新系统缓冲区,目前我们认为,刷新到内核的数据就能到达硬件
解释下为什么cout只打印了一次,因为遇到关键词cout会立即刷新缓冲区


缓冲区存在FILE中,里面有打开文件的缓冲区字段和维护信息
所以之前讲的exit(),函数是刷新C语言缓冲区,在调用_exit()
_exit()直接退出,缓冲区也不刷新