目录
1. 理解文件
(1)文件是在磁盘中的,磁盘是永久性存储介质,所以文件在磁盘上的存储是永久性的。磁盘是外设,即输入输出设备,所以对磁盘上的文件的所有操作,本质上都是对外设的输入和输出,简称IO。
(2)Linux系统下一切皆文件,如键盘,显示器,网卡,磁盘等,系统都会对其进行管理起来。
(3)文件 = 文件属性(元数据)+ 文件内容。对于0KB的空文件也是要占用磁盘空间,所有的文件操作本质上就是对文件内容和文件属性进行操作。
(4)对文件的操作都是正在运行的程序进行操作的,所以对文件的操作就是进程对文件的操作。但是文件在磁盘中,磁盘的管理者是操作系统,所以对磁盘中的文件进行操作绕不开操作系统。文件的读写本质不是通过C/C++的库函数来操作的,而是通过文件相关的系统调用接口实现的,C/C++的库函数只是封装了这些系统调用接口,使用户用起来更加的方便。
2. C语言中文件操作的接口
2.1 打开文件 -- fopen
#include <stdio.h>
int main()
{
FILE *fp = fopen("myfile", "w"); //以‘w’的方式打开myfile文件
if(!fp) {
printf("fopen error!\n");
}
while(1);
fclose(fp);
return 0;
}
打开的myfile文件默认是在当前程序的工作目录下的,myfile文件的绝对路径就是当前程序的工作目录 + 文件名。
使用 ls /proc/进程id -l ,命令查看当前正在运⾏进程的信息:
cwd:指向当前进程运行目录的一个符号链接。
exe:指向启动当前进程的可执行文件(完整路径)的符号链接。
打开文件是进程打开的,进程知道自己在哪个工作目录下,所以文件不带路径,进程默认会打开工作目录下的文件。以 ‘w’ 的方式打开即使没有对应的文件,也会在该进程的工作目录下创建一个。
2.2 写文件 -- fwrite
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp = fopen("result", "w");
if(!fp){
printf("fopen error!\n");
}
const char *msg = "hello xiaoc!\n";
int count = 5;
while(count--){
fwrite(msg, strlen(msg), 1, fp);
}
fclose(fp);
return 0;
}
上述fwrite接口的意思是向 fp 中写入 1个 大小为 strlen(msg) 的 msg 。 循环写五次,得到如下结果:
2.3 读文件 -- fread
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp = fopen("result", "r");
if(!fp)
{
printf("fopen error!\n");
return 1;
}
while(1)
{
//注意返回值和参数,仔细查看man⼿册关于该函数的说明
char buf[1024];
memset(buf, 0 ,sizeof(buf));
size_t s = fread(buf, sizeof(buf)-1, 1, fp);
printf("%s", buf);
if(feof(fp))
{
break;
}
}
fclose(fp);
return 0;
}
2.4 输出信息到显示器的几种方法
#include <stdio.h>
#include <string.h>
int main()
{
printf("hello printf\n");
fprintf(stdout, "hello fprintf\n");
const char *msg = "hello fwrite\n";
fwrite(msg, strlen(msg), 1, stdout);
return 0;
}
这里列举了三种方法,分别是printf,fprintf,fwrite。
2.5 stdin,stdout,stderr
C程序会默认打开三个输入输出流,分别是 stdin,stdout,stderr。这三个流的类型都是FILE*。
stdin:标准输入 -- 键盘文件。
stdout:标准输出 -- 显示器文件。
stderr:标准错误 -- 显示器文件。
知识点1:
为什么要默认打开这三个输入输出流?
因为程序是用来做数据处理的,需要有一个数据输入源和数据输出地,所以在默认情况下会打开这三个输入输出流作为输入源和输出地。
3. 系统文件IO
使用fopen,ifstream这种打开文件的方式是语言层的方案,其实这些语言层的函数都封装了系统层面的文件操作函数,底层都是调用系统的接口进行操作的。
3.1 一种传递标志位的方法 -- 位图
#include <stdio.h>
#define ONE 1 // 0000 0001
#define TWO 2 // 0000 0010
#define THREE 4 // 0000 0100
void func(int flags)
{
if (flags & ONE) printf(" flags has ONE! ");
if (flags & TWO) printf(" flags has TWO! ");
if (flags & THREE) printf(" flags has THREE! ");
printf("\n");
}
int main()
{
func(ONE);
func(THREE);
func(ONE | TWO);
return 0;
}
位图的定义就是使用一个变量中的比特位来当作标志位。如上述代码,定义的ONE,TWO,THREE对应的就是变量中比特位的第0位,第1位,第2位为 1。然后在func函数中使用与运算就可以判断传递的flags中是否存在上述标志位。如果想传递多个标志位,就可以在传递参数的时候将多个标志位进行或操作。
3.2 接口介绍
3.2.1 打开文件 -- open
#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。
O_RDONLY:只读打开。
O_WRONLY:只写打开。
O_RDWR:读,写打开。
这三个常量,必须指定⼀个且只能指定⼀个。
O_CREAT:若⽂件不存在,则创建它。需要使⽤mode选项,来指明新⽂件的访问权限。
O_APPEND:追加写。
mode:指定创建文件时的默认权限。
返回值:
成功:返回新打开的⽂件描述符。
失败:返回 -1。
3.2.2 写文件 -- write
下面代码中open函数传入的参数表示:以只读(O_WRONLY),清除(O_TRUNC)的方式打开文件,若文件不存在则创建该文件(O_CREAT),并把默认权限设置为666。
只写入一次,write函数表示向 fd 文件描述符指定的文件中写入大小为 strlen(msg)的msg。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
//设置文件权限的掩码,如果没有设置使用系统中的文件权限掩码
umask(0);
//对应C语言的w方式打开
int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);