✨个人主页: 熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】
目录
1、回顾C语言文件接口
1.1、以写的方式打开文件
1.2、以追加的方式打开文件
2、初步理解文件
2.1、C++文件接口
3、进一步理解文件
3.1、系统调用实现写方式打开文件
3.2、系统调用实现追加打开文件
3.3、理解open第二个参数原理
1、回顾C语言文件接口
1.1、以写的方式打开文件
1、如果文件不存在,就在当前路径下新建指定的文件。
2、默认打开文件的时候,会先清空文件 -> 等价于 >输出重定向。
代码演示一
#include<stdio.h>int main()
{// 以写方式打开文件FILE* fd = fopen("log.txt","w");if(fd == NULL) {perror("fopen");return 1;}// 向文件写内容fprintf(fd,"helloworld,%d,%s,%lf\n",10,"abcd",3.14);// 关闭文件fclose(fd);return 0;
}
运行结果
代码演示二
#include<stdio.h>int main()
{// 以写方式打开文件FILE* fd = fopen("log.txt","w");if(fd == NULL) {perror("fopen");return 1;}fclose(fd);return 0;
}
运行结果
看一段命令
[jkl@host file]$ cat log.txt
helloworld,10,abcd,3.140000
[jkl@host file]$ echo "hello linux" > log.txt
[jkl@host file]$ cat log.txt
hello linux
[jkl@host file]$ > file.txt
[jkl@host file]$ ls
file.txt log.txt makefile myfile myfile.c
[jkl@host file]$ cat log.txt
hello linux
[jkl@host file]$ >log.txt
[jkl@host file]$ cat log.txt
[jkl@host file]$
解析命令
结论:
重定向 > 可以创建新文件和清空文件内容。
1.2、以追加的方式打开文件
1、如果文件不存在,就在当前路径下新建指定的文件。
2、默认打开文件的时候,会在末尾追加内容 -> 等价于 >>输出重定向。
代码演示
#include<stdio.h>int main()
{// 以追加的方式打开文件FILE* fd = fopen("log.txt","a");if(fd == NULL) {perror("fopen");return 1;}fprintf(fd,"helloworld,%d,%s,%lf\n",10,"abcd",3.14);fclose(fd);return 0;
}
运行结果
看一段命令
[jkl@host file]$ ./myfile
[jkl@host file]$ cat log.txt
helloworld,10,abcd,3.140000
helloworld,10,abcd,3.140000
helloworld,10,abcd,3.140000
[jkl@host file]$ cat myfile.c
[jkl@host file]$ echo "hello linux" >> log.txt
[jkl@host file]$ cat log.txt
helloworld,10,abcd,3.140000
helloworld,10,abcd,3.140000
helloworld,10,abcd,3.140000
hello linux
[jkl@host file]$ >> file1.txt
[jkl@host file]$ ls
file1.txt file.txt log.txt makefile myfile myfile.c
解析命令
结论:
>> 追加重定向可以创建新文件和追加文件内容。
2、初步理解文件
- 文件 = 属性 + 内容
- 打开文件:本质是进程(struct task_struct)打开文件(struct xxx)
- 文件没有打开的时候,存放在哪里?硬盘。
- 进程能打开很多文件吗?可以
- 系统能否存在很多进程?可以
- 很多情况下,OS内部存在大量被打开的文件 -> OS是否要将被打开的文件进行管理 -> 怎么管理呢?先描述在组织 -> 预言:每一个被打开的文件,在OS内部,一定要存在对应的描述文件属性的结构体,类似于PCB。
2.1、C++文件接口
以写的方式打开文件。
代码演示
#include<iostream>
#include<fstream>
#include<string>#define FILENAME "log.txt"int main()
{// out写方式 in读方式 app追加 binary二进制std::ofstream out(FILENAME,std::ios_base::out);// is_open() 检查文件是否打开if(!out.is_open()) return 1; //打开失败,结束程序std::string msg = "hello C++!\n";// ostream& write (const char* s, streamsize n);out.write(msg.c_str(),msg.size());// 关闭文件out.close();return 0;
}
运行结果
3、进一步理解文件
操作文件:本质是进程在操作文件,即进程与文件的关系。
文件 ->存储在硬盘中 -> 硬盘是一个外设 -> 外设是一个硬件 -> 向文件中写入本质是向硬件中写入 -> 用户没有权利直接写入 -> OS是硬件的管理者 -> 通过OS写入 -> OS必须给我们提供系统调用(因为OS不相信任何人) -> fopen/fwrite/fread/fclose ... -> 我们用的都是C/C++/... 对系统调用接口的封装! -> 访问文件,我们也可以直接使用系统调用!!!
为什么要对系统调用的接口进行封装?怎么封装?
后序回答。
使用系统调用接口来操作文件!!!
系统调用接口函数
打开文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);pathname: 要打开或创建的目标文件。
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
mode:设置默认权限信息。
参数:O_RDONLY: 只读打开O_WRONLY: 只写打开O_RDWR : 读,写打开这三个常量,必须指定一个且只能指定一个O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限O_APPEND: 追加写O_TRUNC : 若文件存在会先清空文件
返回值:成功:新打开的文件描述符失败:-1
向文件中写内容
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
将缓冲区指向的 buf 内容中的count个字节数写入到文件描述符 fd 引用的文件。
关闭文件
#include <unistd.h>
int close(int fd);
关闭文件描述符 fd 引用的文件。
3.1、系统调用实现写方式打开文件
系统调用可能用到的头文件
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
代码演示一(不设置权限信息)
int main()
{int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC);if(fd < 0){perror("open");return 1;}// ssize_t write(int fd, const void *buf, size_t count);const char* msg = "hello linux file!\n";write(fd,msg,strlen(msg));close(fd);return 0;
}
运行结果
上面的代码演示的是文件不存在,在当前路径创建指定文件,但没有设置默认权限的情况。
代码演示一(只设置权限信息)
int main()
{int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);if(fd < 0){perror("open");return 1;}// ssize_t write(int fd, const void *buf, size_t count);const char* msg = "hello linux file!\n";write(fd,msg,strlen(msg));close(fd);return 0;
}
运行结果
上面的代码演示的是文件不存在,在当前路径创建指定文件,且设置默认权限的情况。
想要第三个参数是什么权限文件就是什么权限应该怎么办呢???
将umask值设置为0即可。
#include <sys/types.h>
#include <sys/stat.h>mode_t umask(mode_t mask);
设置umask值。
代码演示三(设置权限信息和umask)
int main()
{umask(0);int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);if(fd < 0){perror("open");return 1;}// ssize_t write(int fd, const void *buf, size_t count);const char* msg = "hello linux file!\n";write(fd,msg,strlen(msg));close(fd);return 0;
}
运行结果
上面的代码演示的是文件不存在,在当前路径创建指定文件,且设置默认权限和umask的情况。
代码演示四
int main()
{umask(0);int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0777);if(fd < 0){perror("open");return 1;}close(fd);return 0;
}
运行结果
上面的代码演示的是文件存在,先清空文件内容,即使修改默认权限,也不会影响该文件的权限。
3.2、系统调用实现追加打开文件
代码演示
int main()
{umask(0);int fd = open("log.txt",O_WRONLY | O_CREAT | O_APPEND,0666);if(fd < 0){perror("open");return 1;}// ssize_t write(int fd, const void *buf, size_t count);const char* msg = "hello linux file!\n";write(fd,msg,strlen(msg));close(fd);return 0;
}
运行结果
3.3、理解open第二个参数原理
原理是位图加位运算,下面通过一个程序理解调用open第二个参数原理。
代码演示
#define ONE 1 // 0000 0001
#define TWO (1<<1) // 0000 0010
#define THREE (1<<2) // 0000 0100
#define FOUR (1<<3) // 0000 1000void print(int flag)
{if(flag & ONE){printf("one\n");}if(flag & TWO){printf("two\n");}if(flag & THREE){printf("three\n");}if(flag & FOUR){printf("four\n");}
}
int main()
{print(ONE);printf("\n");print(TWO);printf("\n");print(ONE | TWO);printf("\n");print(ONE | TWO | THREE);printf("\n");print(ONE | TWO | THREE | FOUR);printf("\n");return 0;
}
运行结果