fopen,fwrite,fread,fseek,fclose这些函数都是库函数,是c库当中提供能给程序员调用的函数。
函数描述:
path:待打开的文件(文件路径+文件名称)
mode:以何种方式打开
返回值:打开成功返回文件流指针,打开失败返回NULL。
示例:
#include
#include
#include
int main()
{
//以只读方式打开文件linux
FILE *fp = fopen("./linux","r"); // 不管是r还是r+,如果文件不存在都会打开失败
if(!fp)
{
perror("fopen");
return -1;
}
printf("open success\n");
return 0;
}
//输出结果:
fopen: No such file or directory
创建linux文件,再次以只读方式打开,结果如下:
[test@localhost c_file]$ touch linux
[test@localhost c_file]$ ./test_file
open success
#include
#include
#include
int main()
{
FILE *fp = fopen("./linux","w"); //不管是w还是w+,如果文件不存在都会创建文件
//如果存在,则清空文件
if(!fp)
{
perror("fopen");
return -1;
}
printf("open success\n");
return 0;
}
//输出结果:
open success
此时查看文件:
[test@localhost c_file]$ make
gcc test.c -o test_file
[test@localhost c_file]$ ./test_file
open success
[test@localhost c_file]$ ls
linux makefile test.c test_file
现在linux文件存在,往这个文件中随便写入数据,然后再次以只写方式打开文件:
[test@localhost c_file]$ cat linux
Hello World
[test@localhost c_file]$ ./test_file
open success
[test@localhost c_file]$ cat linux
[test@localhost c_file]$ #文件内容已经被清空
#include
#include
#include
int main()
{
FILE *fp = fopen("./linux","a"); //不管是a还是a+,如果文件不存在都会创建文件
//如果存在,当前文件流指针指向文件末尾,并不会清空文件内容
if(!fp)
{
perror("fopen");
return -1;
}
printf("open success\n");
return 0;
}
//输出结果:
open success
现在linux文件存在,往这个文件中随便写入数据,然后再次以追加方式打开文件:
[test@localhost c_file]$ echo "Hello world" >> linux
[test@localhost c_file]$ cat linux
Hello world
[test@localhost c_file]$ ./test_file
open success
[test@localhost c_file]$ cat linux
Hello world #可以看到文件内容没有被清空
函数描述:
ptr:要往文件当中写的内容
size:写入块的大小,单位是字节
nmemb:块的个数,单位是个(写入文件字节的数量:size * nmemb)
注意:一般在程序中使用时,是将size设置为1,则nmemb就表示写入的字节数量。
stream:文件流指针
返回值:返回写入成功块的个数,切记不是写入成功字节的数量。
示例:
#include
#include
#include
#include
#include
int main()
{
FILE *fp = fopen("./linux","w+"); //以读写方式打开文件
if(!fp)
{
perror("fopen");
return -1;
}
printf("open success\n");
//创建buf数组用于保存需要往文件写入的数据
char buf[1024] = {
0};
const char *ptr = "Hello World!";
strncpy(buf,ptr,strlen(ptr));
size_t ret = fwrite(buf,1,strlen(ptr),fp);
printf("ret:%d\n",ret); // 返回的是写入成功块的个数
return 0;
}
输出结果:
[test@localhost c_file]$ ./test_file
open success
ret:12
[test@localhost c_file]$ cat linux
Hello World![test@localhost c_file]$
函数描述:
ptr:要将读到的内容保存在哪里
size:每次读块的大小
nmemb:块的个数
stream:文件流指针
返回值:成功读到的块的个数,返回0说明读取成功了但是没有读到内容。
示例:
#include
#include
#include
#include
#include
int main()
{
FILE *fp = fopen("./linux","r");
if(!fp)
{
perror("fopen");
return -1;
}
printf("open success\n");
// 创建数组用以保存读取的数据
char buf[1024] = {
0};
size_t ret = fread(buf,1,sizeof(buf)-1,fp);
printf("buf:%s\n",buf);
printf("ret:%d\n",ret); // 返回的是读到的块的个数
return 0;
}
输出结果:
[test@localhost c_file]$ ./test_file
open success
buf:Hello world
ret:12
函数描述:
stream:文件流指针
offset:偏移量
whence:
示例:
在一个程序中在文件写入数据后,想要继续读取数据,此时就用到了fseek函数
不使用fseek:
#include
#include
#include
#include
#include
int main()
{
FILE *fp = fopen("./linux","w+");
if(!fp)
{
perror("fopen");
return -1;
}
printf("open success\n");
char buf_w[1024] = {
0};
const char *ptr = "Hello World!";
strncpy(buf_w,ptr,strlen(ptr));
size_t ret_w = fwrite(buf_w,1,strlen(ptr),fp);
printf("ret_w:%d\n",ret_w);
//fseek(fp,0,SEEK_SET); //将文件流指针偏亮到文件头,偏移量为0
char buf_r[1024] = {
0};
size_t ret_r = fread(buf_r,1,sizeof(buf_r)-1,fp);
printf("buf_r:%s\n",buf_r);
printf("ret_r:%d\n",ret_r);
return 0;
}
输出结果:
[test@localhost c_file]$ ./test_file
open success
ret_w:12
buf_r: #此时文件流指针指在文件末尾,所以没有读取到
ret_r:0 # 返回值为-1,表示fread函数调用错误,为0,则表示读取成功了,但是没有读到内容
使用fseek后:
[test@localhost c_file]$ ./test_file
open success
ret_w:12
buf_r:Hello World!
ret_r:12
[test@localhost c_file]$ cat linux
Hello World![test@localhost c_file]$
关闭文件流指针
fclose(fp); // fp 文件流指针
—— 系统调用函数的操作文件接口
open,write,read,lseek,close这些函数都是系统调用,是操作系统内核为程序员提供的函数
函数描述:
pathname:要打开的文件名称(路径+名称)
flags:以何种方式打开
必须的宏,三个宏有且只能出现一个
可选的宏
使用方式:必须的宏和可选的宏之间使用按位或的方式(部分)
例:O_RDWR | O_CREAT (是按照位图的方式来使用的)
mode:权限,给新创建出来的文件设置权限,传参的时候,传八进制数字就可以了。
返回值:打开成功,返回大于等于0的数字,是文件描述符,打开失败,返回-1。
示例:
#include
#include
#include
#include
#include
int main()
{
int fd = open("./linux",O_RDWR | O_CREAT,0664);
if(fd<0)
{
perror("open");
return -1;
}
printf("open success\n");
printf("fd:%d\n",fd);
return 0;
}
输出结果:
[test@localhost sys_file]$ ./sys_file
open success
fd:3
[test@localhost sys_file]$ ls
linux makefile sys_file test.c
函数描述:
fd:文件描述符,open的返回值
buf:往文件里写的内容
count:写的内容的大小
返回值:写成功的字节数量
示例:
#include
#include
#include
#include
#include
int main()
{
int fd = open("./linux",O_RDWR | O_CREAT,0664);//以读写方式打开文件,如果文件不存在,则创建
if(fd<0)
{
perror("open");
return -1;
}
printf("open success\n");
printf("fd:%d\n",fd);
char buf[1024] = {
0};
const char *ptr = "Hello World!";
strncpy(buf,ptr,strlen(ptr));
write(fd,buf,strlen(ptr));
return 0;
}
输出结果:
[test@localhost sys_file]$ ./sys_file
open success
fd:3
[test@localhost sys_file]$ cat linux
Hello World![test@localhost sys_file]$
函数描述:
fd:文件描述符,open的返回值
buf:要将读到的内容放到哪里去
count:最大可以读多少个单位字节
返回值:返回读到的字节数量,返回0说明读取成功了,但是没有读取到内容
示例:
#include
#include
#include
#include
#include
int main()
{
int fd = open("./linux",O_RDWR | O_CREAT,0664);
if(fd<0)
{
perror("open");
return -1;
}
printf("open success\n");
printf("fd:%d\n",fd);
char buf[1024] = {
0};
read(fd,buf,sizeof(buf)-1); //-1 是为了给 '\0'留一个位置
printf("buf:%s\n",buf);
return 0;
}
输出结果:
[test@localhost sys_file]$ ./sys_file
open success
fd:3
buf:Hello World!
函数描述:
fd:文件描述符
offset:偏移量
whence:
示例:
#include
#include
#include
#include
#include
int main()
{
//打开文件
int fd = open("./linux",O_RDWR | O_CREAT,0664);
if(fd<0)
{
perror("open");
return -1;
}
printf("open success\n");
printf("fd:%d\n",fd);
//写
char buf_w[1024] = {
0};
const char *ptr = "Hello World!";
strncpy(buf_w,ptr,strlen(ptr));
write(fd,buf_w,strlen(ptr));
//lseek(fd,0,SEEK_SET); //将文件流指针偏移到文件头
//读
char buf_r[1024] = {
0};
read(fd,buf_r,sizeof(buf_r)-1); //-1 是为了给 '\0'留一个位置
printf("buf_r:%s\n",buf_r);
return 0;
}
不使用lseek输出结果:
[test@localhost sys_file]$ ./sys_file
open success
fd:3
buf_r: #此时文件流指针指在文件末尾,所以没有读取到
使用lseek之后:
[test@localhost sys_file]$ ./sys_file
open success
fd:3
buf_r:Hello World!
关闭文件描述符
close(fd); //fd 文件描述符
close(0); // 关闭标准输入
close(1); // 关闭标准输出
close(2); // 关闭标准错误
通过对open函数的学习与理解,文件描述符fd就是一个整数。
操作系统会为每一个进程在磁盘上创建一个以进程号命名的文件夹,在该文件夹下有一个fd文件夹,保存的信息即为该进程打开的文件描述符信息。
下面来一段代码演示:
#include
#include
#include
#include
int main()
{
int fd = open("./linux",O_RDWR | O_CREAT,0644);//打开文件
if(fd<0)
{
perror("open");
return -1;
}
printf("fd:%d\n",fd);
while(1)
{
sleep(1);
}
return 0;
}
//输出结果
fd:3 //然后程序死循环,方便查看信息
查看该进程的文件描述符信息:
可以看到,当我们新创建出来一个进程,势必会打开3个文件描述符,分别对应,标准输入 (0),标准输出 (1),标准错误 (2)。
如图所示,当 ./main运行该程序时:
可以看到,文件描述符其实就是在内核当中的fd_array数组的下标。
通过上面的代码发现,打开新的文件后,fd的值为3,那么我们关闭0或者2再看
示例代码:
#include
#include
#include
#include
int main()
{
close(0);
int fd = open("./linux",O_RDWR | O_CREAT,0644);//打开文件
if(fd<0)
{
perror("open");
return -1;
}
printf("fd:%d\n",fd);
while(1)
{
sleep(1);
}
return 0;
}
//输出结果:
fd:0 //然后程序死循环,方便查看信息
如图所示:关闭0以后
如图所示:关闭2以后
结论:文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符即最小未占用原则。
当我们打开一个文件,操作系统会给程序分配一个文件描述符,如果在使用完毕之后,没有及时关闭文件,就会造成文件句柄泄露。
#include
#include
#include
int main()
{
int fd_count=0;
while(1)
{
int fd = open("./linux",O_RDWR | O_CREAT,0664); // 一直打开文件
if(fd<0)
{
perror("open");
break;
}
printf("fd:%d\n",fd);
fd_count++;
}
printf("fd_count:%d\n",fd_count);
return 0;
}
输出结果:
[test@localhost fd]$ ./fd_count
fd:3
fd:4
fd:5
fd:6
fd:7
……
fd:1019
fd:1020
fd:1021
fd:1022
fd:1023
open: Too many open files #到1024就停下来了
fd_count:1021 #从文件描述符3开始到1023
[test@localhost fd]$
由结果可得知:fd从3开始是因为新创建出来一个进程,操作系统势必会打开3个文件描述符,即 0(标准输入)、1(标准输出)、2(标准错误)
[test@localhost dup]$ echo "Hello" > linux #将Hello重定向到linux文件中
[test@localhost dup]$ cat linux
Hello
[test@localhost dup]$ echo "linux" > linux #清空linux文件内容,再把linux重定向到linux文件中
[test@localhost dup]$ cat linux
linux
[test@localhost dup]$
[test@localhost dup]$ echo "Hello" >> linux
[test@localhost dup]$ cat linux
linux
Hello
[test@localhost dup]$ #并没有清空文件内容而是追加在后面
流程:
代码演示:
#include
#include
#include
#include
int main()
{
//打开文件,不存在则创建
int fd = open("./linux",O_CREAT | O_RDWR,0664);
if(fd<0)
{
perror("open");
}
printf("fd:%d\n",fd);
//将标准输出重定向到文件当中
//dup2(int oldfd,int newfd)
//oldfd --> fd(上面打开文件的文件描述符) newfd --> 1(标准输出)
// dup2(fd,1);
int ret_d = dup2(fd,1);
printf("ret_d:%d\n",ret_d);//返回的是1这个文件描述符
//成功以后,下面的代码在往标准输出当中进行输出的时候,就是往文件当中写了
printf("Hello World\n");
// close(fd);
while(1)
{
sleep(1);
}
return 0;
}
//输出结果:
fd:3 //之后陷入死循环,为了方便查看文件描述信息
可以发现已经将标准输出重定向到了文件当中,验证一下是否打印到了文件当中
[test@localhost dup]$ cat linux
ret_d:1
Hello World #可以看到dup2函数之后的两条打印语句都打印到了linux这个文件中