用 fopen
打开文件,返回类型为 FILE *
,其是一个结构体,包含
读写文件不是直接写入或读磁盘,而是先写入内存中的缓冲区,待缓冲区满了才放入磁盘中。
IO缓冲区的作用?
而将缓冲区的数据刷新到磁盘有三种方式
fflush
刷新缓冲区fclose
、return(main函数)
、exit (main函数)
C标准函数调用Linux系统的API,然后调用内核层设备驱动函数通过设备驱动操作硬件。
内核区存在PCB进程控制块,其包含文件描述符表,0~2是默认打开的,之后每打开一个新文件,则占用一个文件描述符,而且使用的是空间的最小的一个文件描述符。
char* argv[]
获取malloc
申请的空间就存放在这当然虚拟地址空间是4G并不代表程序实际使用了4G的内存空间,虚拟地址空间会映射到实际的内存空间中。
虚拟内存的存在主要有三个作用:
open
打开未存在的文件。#include
#include
#include
#include
#include
#include
int main()
{
int fd;
// 打开不存在的文件
fd = open("bucunz", O_RDWR);
if(fd == -1)
{
perror("open file");
exit(1);
}
// 关闭文件
int ret = close(fd);
printf("ret = %d\n", ret);
if(ret == -1)
{
perror("close file");
exit(1);
}
}
这里 open
的第一个参数表示要打开的文件,O_RDWR
表示可读可写,其他还有 O_RDONLY
只读打开以及 O_WRONLY
只写打开,其返回文件描述符,如果出现错误则返回 -1
,这里使用 perror
在错误输出之前输出里面的内容。关闭文件使用 close
其参数为文件描述符,如果产生错误返回 -1
,成功则返回 0
。
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
// 创建新文件
fd = open("myhello", O_RDWR | O_CREAT, 0777);
if(fd == -1)
{
perror("open file");
exit(1);
}
printf("fd = %d\n", fd);
// 关闭文件
int ret = close(fd);
printf("ret = %d\n", ret);
if(ret == -1)
{
perror("close file");
exit(1);
}
}
这里通过在 open
中添加参数 O_CREAT
以在没有该文件时生成新文件。
可以看到其 fd
为 3,前面 0~2
为默认打开的,所以从 3
开始分配。
我们之前设置最后参数为 0777
表示赋予文件所有用户所有权限,但我们看到该文件的其他用户并没有写权限,这是因为Linux在赋予文件全选时需要减去一个掩码,可以通过 umask
显示,这里为0002,其取反与赋予的权限通过二进制与操纵产生最终的文件权限。
创建新文件,如果文件已存在则出错返回
可以在 open
参数添加 O_EXCL
实现,即fd = open("myhello", O_RDWR | O_CREAT | O_EXCL, 0777);
如果文件存在,将长度截断为0字节
可以在 open
参数添加 O_TRUNC
实现,即fd = open("myhello", O_RDWR | O_TRUNC);
read
的函数原型如下:
其返回值有三种可能:
write
的函数原型如下:
如果写成功,返回写入的字节数,如果出错则返回 -1。
read
与 write
函数均需要指定读取或写入文件的文件描述符,缓冲区,以及读取和写入的字节数。
下面使用 open
,read
,write
实现从一个文件读取数据放入另一个文件中:
#include
#include
#include
#include
#include
#include
int main(void)
{
int fd = open("test1.txt", O_RDONLY);
if(fd == -1)
{
perror("open file1");
exit(1);
}
int fd2 = open("test2.txt", O_WRONLY | O_CREAT, 0777);
if(fd2 == -1)
{
perror("open file2");
exit(1);
}
int size;
char buffer[8]; // 缓冲区
while((size=read(fd, buffer, 8)) != 0)
{
if(size == -1)
{
perror("read file1");
exit(1);
}
int ret = write(fd2, buffer, size);
if(ret == -1)
{
perror("write file2");
exit(1);
}
printf("%d\n", ret);
}
if(close(fd) == -1)
{
perror("close file1");
exit(1);
}
if(close(fd2) == -1)
{
perror("close file2");
exit(1);
}
return 0;
}
可以看到 test1.txt
一共50个字节,前六次每次读取缓冲区大小的字节数(8),最后一次读取最后2个字节。
lseek
函数原型如下:
lseek
重新定位文件偏移,offset
表示偏移量,whence
表示偏移位置,即从哪开始偏移。
whence
共有三种选项:
SEEK_SET
文件偏移被设为 offset
,即从文件头开始偏移 offset
字节SEEK_CUR
文件偏移被设为文件指针当前位置+offset
偏移字节数,即从当前位置开始偏移offset
字节SEEK_END
文件偏移被设为文件的大小+offset
偏移字节数,即从文件尾开始偏移offset
字节这里需要说明的是,当从文件尾开始偏移offset
字节时,这时并不会改变文件大小,如果此时在该位置进行写入,那么中间部分会变成空洞,进行占位,除非数据被写入这些空洞,否则返回 \0
。空洞的作用有一点就是当我们下载文件时,用空洞进行占位可以需要的空间保留下来。
如果成功返回从文件头开始的偏移字节数,如果产生错误,返回 -1。
#include
#include
#include
#include
#include
#include
int main(void)
{
int fd = open("./test1.txt",O_RDWR);
if( fd == -1)
{
perror("open test1.txt:");
exit(1);
}
int ret = lseek(fd,0,SEEK_END);
printf("file length = %d\n",ret);
// 文件扩展
ret = lseek(fd,2000,SEEK_END);
printf("return value = %d\n",ret);
// 实现文件拓展,需要最后一次写操作
write(fd,"a",1);
close(fd);
return 0;
}
可以看到 test1.txt
被扩展为 2051 字节了,且中间有很多空洞 ^@