【Linux系统编程】第二十六弹---彻底掌握文件I/O:C/C++文件接口与Linux系统调用实践

✨个人主页:熬夜学编程的小林

💗系列专栏: 【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 1000

void 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;
}

运行结果

相关推荐
敲代码娶不了六花8 分钟前
jsp | servlet | spring forEach读取不了对象List
java·spring·servlet·tomcat·list·jsp
彩虹糖_haha9 分钟前
Linux高并发服务器开发 第五天(压缩解压缩/vim编辑器/查找替换/分屏操作/vim的配置)
linux·运维·服务器
旺仔学IT9 分钟前
Centos7中使用yum命令时候报错 “Could not resolve host: mirrorlist.centos.org; 未知的错误“
linux·运维·centos
Yhame.9 分钟前
深入理解 Java 中的 ArrayList 和 List:泛型与动态数组
java·开发语言
编程之路,妙趣横生11 分钟前
list模拟实现
c++
qq_433618441 小时前
shell 编程(五)
linux·运维·服务器
是小崔啊2 小时前
开源轮子 - EasyExcel02(深入实践)
java·开源·excel
VVVVWeiYee2 小时前
项目2路由交换
运维·服务器·网络·网络协议·信息与通信
myNameGL2 小时前
linux安装idea
java·ide·intellij-idea
88号技师2 小时前
2024年12月一区SCI-加权平均优化算法Weighted average algorithm-附Matlab免费代码
人工智能·算法·matlab·优化算法