【linux】:linux下文件的使用以及文件描述符(上)

简介: 【linux】:linux下文件的使用以及文件描述符(上)

前言



理解文件原理和操作:

我们先快速回忆下一C语言的文件操作:

4b9bc1ffdc624addb785792f86abc3c1.png


首先看一下fopen函数的使用,然后我们写一段简单的C语言打开文件的代码如下图:

#include <stdio.h>
#define LOG "log.txt"
int main()
{
  //默认写方式打开文件,如果文件不存在,就删除它
  FILE* fp = fopen(LOG,"w");
  if (fp==NULL)
  {
    perror("fopen:");
    return 1;
  }
  //正常进行文件操作
  const char* msg = "hello friends\n";
  int cnt = 1;
  while (cnt)
  {
    fputs(msg,fp);
    --cnt;
  }
  fclose(fp);
  return 0;
}


接下来我们运行一下:

050b5363cb0946739adca264abd63702.png

通过上图我们可以看到成功将我们的字符串写入文件中,那么这次我们什么都不写再看看:

fdf2d420d4ca4888b00b90c5d7a6607c.pngf71a0ec33b994ff58bd9cb0ddc78d4a8.png


为什么原先log里的字符串没了呢?因为我们打开的方式是w,文件内容会先被清空再写入。

下面我们再介绍一个接口snprintf:

2b6de12b9b544b71a13fef0743c45ad8.png


普通的printf是默认向显示器打印,fprintf是指定文件流向指定文件进行打印,下面我们演示一下:

首先我们修改一下代码,将fputs替换为fprintf函数

int main()
{
  //默认写方式打开文件,如果文件不存在,就删除它
  FILE* fp = fopen(LOG,"w");
  if (fp==NULL)
  {
    perror("fopen:");
    return 1;
  }
 //正常进行文件操作
  const char* msg = "hello friends\n";
  int cnt = 5;
  while (cnt)
  {
   // fputs(msg,fp);
    fprintf(fp,"%s:%d:sxy\n",msg,cnt);
    --cnt;
  }
  fclose(fp);
  return 0;
}


ac8e727dc6814dbca0f0f0c8fdbc22b7.png

运行结果如上图所示,并且我们不仅可以像文件中打印,也可以在输出流中打印:

14126e135e0a4a7ba4270da5e628e96a.png


我们都知道,一个程序会默认打开以上三个输入输出流,下面我们像stdout打印一下:ae9c660e6b3f4b399410842b31deb5b7.png93f26cbf0ca149c298296e0c1d2f1c2c.png


运行后我们发现直接就能显示不用再打开log.txt查看了,下面我们再来演示一下snprintf函数:

int main()
{
  //默认写方式打开文件,如果文件不存在,就删除它
  FILE* fp = fopen(LOG,"w");
  if (fp==NULL)
  {
    perror("fopen:");
    return 1;
  }
 //正常进行文件操作
  const char* msg = "hello friends\n";
  int cnt = 5;
  while (cnt)
  {
    char buffer[256];
    snprintf(buffer,sizeof(buffer),"%s:%d:sxy\n",msg,cnt);
    fputs(buffer,fp);
    --cnt;
  }
  fclose(fp);
  return 0;
}



snprintf就是将msg格式化到buffer数组里并且以buffer的大小进行打印,然后将数组中的数据写入文件中。

2d6981692cb7420088ce6b6c2bbd068c.png


后面还有a追加写入等的方式我们就不在演示,这些都是我们C语言的时候学过的。

以上都是语言进行文件的操作,下面我们进入今天的正题,在系统层面使用文件操作。


一、linux系统中的文件操作以及文件接口



首先我们看一下系统中文件的接口open:


8dbfb80a3e1c49f0982e2f01fce296cb.png


然后我们再看一下这个接口的返回值是什么:

3abe030b766049a589af46f8bb9dd6e0.png


如果成功返回一个新的文件描述符,否则返回-1,下面我们演示一下这个接口:

首先一定要包头文件:

f6f45daaff7f4253bd2d00edece224fe.png


这个函数的第一个参数与刚刚一样都是文件名,第二个参数其实是用位图实现的,就是用一个标志位充当不同的行为,如下图所示:

71dc49ee99a047e8bdb2933b6ed74240.png

在上图中我们就利用位图传不同的flags就能打打印不同的数,并且不仅可以打印一个也可以同时打印多个:

11e7d9104cd04cacbb49edd82e6e8aad.png9cd8b53f8612417e96b7ca5667a85e8e.png


而第二个参数的使用方式与上面演示的一样,都有下面这几种选项:


ded46c57d792467c9c22c1957b0b86bf.png

这些都是宏像我们刚刚演示的#define的那样。下面我们演示这个接口:

//系统层面
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define LOG "log.txt"
int main()
{
  int fd = open(LOG,O_WRONLY);
  printf("fd:%d\n",fd);
  close(fd);
  return 0;
}


0_CREAT是如果没有这个文件就给我们创建一个文件,O_WRONLY是只写的意思。

26eed91ff2a043a1959127329ff92c40.png

我们先删除原先的log文件,运行后发现返回值为-1,也就是说出错了,那是什么原因呢?我们修改一下代码如果出错就打印一下出错原因:

e82987679bca408dbfde187b3bac2f22.png23c4470e521e47c4872fb4fcb2f98c8b.png

因为我们要写这个文件,但是这个文件并不存在,所以我们没有的话应该先创建一个文件:

f9d24ccf8c94494d9440a3ea6dade890.png19d6d81111b44e0bba71a773abfdaddb.png


这次我们发现文件创建成功了,错误码为0。我们这里用的是root所以文件的权限没有问题,如果这里是普通用户创建文件的话文件权限是乱码有问题的,如下图:

54c0640266414b69b5971c6241213e45.png


所以我们一般创建文件用的是三个接口的那个函数,我们演示的这个函数没有提供文件权限的接口。

836cf421c1c744d9966bdafe148f1655.png

下面我们用三个接口的open创建一下文件:

d4a86f29b9e74edfbc2abab990c7ceb1.png63484de293c84158850e4218d952f320.png


我们先删掉之前的文件,然后重新运行一下发现这次文件的权限没有问题是正确的(不是666是因为受umask影响)。所以,一个文件被创建默认权限受umask影响,那么如果想不受umask影响呢?

b200727d86834cedbaafafcbbe8f0703.png


我们可以直接将所有的权限掩码设置为0:

f23d58fa140a4737872ee28e42e13343.png8c6d6e03bf2f431aae700f7b8f38432d.png


我们发现这次就不受umask影响了。接下来我们写入文件试试:

关于写入的文件接口的函数为write:

2e5a4e10ee7b4c5e8af7a8d75fee4944.png


向文件描述符写入一个缓冲区大小为count,返回值是实际写入多少字节,写入失败返回-1.

int main()
{
  umask(0);
  int fd = open(LOG,O_WRONLY | O_CREAT,0666);
  if (fd==-1)
  {
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));
  }
  else printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));
  printf("fd:%d\n",fd);
  const char* msg = "hello friend";
  int cnt = 5;
  while (cnt)
  {
    char line[128];
    snprintf(line,sizeof(line),"%s,%d\n",msg,cnt);
    write(fd,line,strlen(line));
    --cnt;
  }
  close(fd);
  return 0;
}


接下来我们运行一下:

ac5e4beaed3540afbb8a1bc69049302d.png


通过上图我们发现写入成功,其实在我们刚刚strlen的时候发现我们并没有带\0,为什么没有\0也能成功写入呢,因为\0是C语言的标准不是系统的规定,并且\0在系统中写入会变成乱码。


下面我们重新写一下:

7f10b275c7ef477bad949c4a9f737270.pnged5e1f7c5d66420b99d5e6250c48eb19.png


在我们重新运行后发现文件中保留了上一次的数据,也就是说O_CREAT | O_WRONLY是不会清空原来的文件再写入的,这个时候我们加上O_TRUNC选项就可以每次写入前先清空文件了:

1c4eb69d6df44093b2b4b60f2eead12f.png791fa3dc96a54d3fb7fd60fd2f00e5c9.png


搞定了写入那么饿追加就不是问题了,追加只需要注意将写入替换为追加,并且我们不希望每次打开文件先清空文件,所以不需要O_TRUNC选项:

f8e39f73532d4533b43a35eb79d7fde4.png22efbf0b454b4973b77e181f6f253aea.png

为什么我们上面的运行结果不正确呢?因为0_APPEND只是追加不是追加写,所以才没有写入。下面我们修改一下代码:

74ede980ace7475da6d2eafba70c8683.pngf39e110369584cb3ae869902b4a276ca.png

这样我们就成功的追加了字符串,那么如果是只读呢?其实只读就很简单了,只读只需要调用两个接口的open函数即可,因为只读默认认为是有文件的。

9bbef5925f90420b9ed3b558a6f643e5.png


读取的返回值就是当前读取了多少字节,读取到文件结尾就是0,失败就是-1。

int main()
{
  umask(0);
  int fd = open(LOG,O_RDONLY);
  if (fd==-1)
  {
    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));
  }
  else printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));
  char buffer[1024];
  ssize_t n = read(fd,buffer,sizeof(buffer)-1); //使用系统接口来进行IO的时候,一定要注意\0问题。
  if (n>0)
  {
    buffer[n] = '\0';
    printf("%s\n",buffer);
  }
  close(fd);
  return 0;
}


c04ac4cc81d243aabc25cf2e6f80fa10.png


我们在读取的时候为什么-1呢?因为我们不考虑\0的问题,只有当读取的时候返回值大于0我们将\0放到n的位置以字符串的形式打印的时候才需要\0,所以最后需要将\0放到n的位置再打印。

以上就是系统级的文件接口,而入C语言等要进行文件操作都必须调用系统接口。


目录
相关文章
|
7月前
|
Linux 开发工具
7种比较Linux中文本文件的最佳工具
7种比较Linux中文本文件的最佳工具
7种比较Linux中文本文件的最佳工具
|
5月前
|
存储 数据管理 Linux
区分Linux中.tar文件与.tar.gz文件的不同。
总之,".tar"文件提供了一种方便的文件整理方式,其归档但不压缩的特点适用于快速打包和解压,而".tar.gz"文件通过额外的压缩步骤,尽管处理时间更长,但可以减小文件尺寸,更适合于需要节约存储空间或进行文件传输的场景。用户在选择时应根据具体需求,考虑两种格式各自的优劣。
824 13
|
6月前
|
安全 Linux
Linux赋予文件000权限的恢复技巧
以上这些步骤就像是打开一扇锁住的门,步骤看似简单,但是背后却有着严格的逻辑和规则。切记,在任何时候,变更文件权限都要考虑安全性,不要无谓地放宽权限,那样可能
204 16
|
7月前
|
Linux
【Linux】 Linux文件I/O常见操作技巧
以上就是Linux文件I/O操作的一些技巧,接纳它们,让它们成为你在Linux世界中的得力伙伴,工作会变得轻松许多。不过记住,技巧的运用也需要根据实际情况灵活掌握,毕竟,最适合的才是最好的。
240 28
|
6月前
|
存储 Linux 数据处理
深入剖析Linux中一切即文件的哲学和重定向的机制
在计算机的奇妙世界中,Linux的这套哲学和机制减少了不同类型资源的处理方式,简化了抽象的概念,并蕴藏着强大的灵活性。就像变戏法一样,轻轻松松地在文件、程序与设备之间转换数据流,标准输入、输出、错误流就在指尖舞动,程序的交互和数据处理因此变得既高效又富有乐趣。
111 4
|
7月前
|
Ubuntu Linux
"unzip"命令解析:Linux下如何处理压缩文件。
总的来说,`unzip`命令是Linux系统下一款实用而方便的ZIP格式文件处理工具。本文通过简明扼要的方式,详细介绍了在各类Linux发行版上安装 `unzip`的方法,以及如何使用 `unzip`命令进行解压、查看和测试ZIP文件。希望本文章能为用户带来实际帮助,提高日常操作的效率。
1055 12
|
8月前
|
Linux Shell
Linux系统下快速批量创建和删除文件的方法
总的来说,使用shell脚本来批量处理文件是一种非常强大的工具,只要你愿意花时间学习和实践,你会发现它能大大提高你的工作效率。
477 19
|
6月前
|
Linux
linux文件重命名命令
本指南介绍Linux文件重命名方法,包括单文件操作的`mv`命令和批量处理的`rename`命令。`mv`可简单更改文件名并保留扩展名,如`mv old_file.txt new_name.txt`;`rename`支持正则表达式,适用于复杂批量操作,如`rename &#39;s/2023/2024/&#39; *.log`。提供实用技巧如大小写转换、数字序列处理等,并提醒覆盖风险与版本差异,建议使用`-n`参数预览效果。
|
9月前
|
监控 Linux
Linux基础:文件和目录类命令分析。
总的来说,这些基础命令,像是Linux中藏匿的小矮人,每一次我们使用他们,他们就把我们的指令准确的传递给Linux,让我们的指令变为现实。所以,现在就开始你的Linux之旅,挥动你的命令之剑,探索这个充满神秘而又奇妙的世界吧!
177 19
|
8月前
|
缓存 Linux
如何创建Linux交换文件?Linux交换文件最新创建方法
Swap是Linux中的虚拟内存空间,用于在物理内存不足时将非活动进程移至磁盘,从而优化活动进程的性能。通过创建交换文件(如1GB),可灵活调整交换空间而无需重新分区。步骤包括:使用`fallocate`或`dd`创建文件、设置权限 (`chmod 600`)、格式化 (`mkswap`)、启用交换 (`swapon`)、修改`/etc/fstab`以持久化配置,以及调整`vm.swappiness`值(默认60,建议从10开始)来平衡内存与交换的使用。最后通过`swapon -s`检查状态并重启生效。此方法适用于VPS和专用服务器,需以root用户操作。
233 2