【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语言等要进行文件操作都必须调用系统接口。


目录
相关文章
|
3天前
|
Linux 网络安全
linux/服务器使用scp将一个服务器文件转移到另一个服务器上
linux/服务器使用scp将一个服务器文件转移到另一个服务器上
29 3
|
4天前
|
Linux Go
linux ls -la文件信息含义
linux ls -la文件信息含义
9 1
|
4天前
|
安全 Unix Linux
Linux 中的文件描述符是什么?
Linux 中的文件描述符是什么?
13 6
Linux 中的文件描述符是什么?
|
4天前
|
Linux Shell Python
如何计算 Linux 上文件中的空行数?
【5月更文挑战第11天】
12 0
|
4天前
|
Linux API
Linux系统编程之文件编程常用API回顾和文件编程一般步骤
Linux系统编程之文件编程常用API回顾和文件编程一般步骤
Linux系统编程之文件编程常用API回顾和文件编程一般步骤
|
4天前
|
Linux
如何在 Linux 中递归更改文件的权限?
【5月更文挑战第10天】
19 3
|
4天前
|
Linux
Linux如何查询较大文件的方法
【5月更文挑战第8天】Linux如何查询较大文件的方法
15 0
|
4天前
|
Linux 程序员 Shell
【Linux】详解core dump文件的作用以及用法&&ubuntu20.04下无法形成core dump文件的解决办法
【Linux】详解core dump文件的作用以及用法&&ubuntu20.04下无法形成core dump文件的解决办法
|
4天前
|
Linux Shell
Linux操作系统下查找大文件或目录的技巧
Linux操作系统下查找大文件或目录的技巧
12 2
|
4天前
|
算法 Linux
Linux:文件增删 & 文件压缩指令
Linux:文件增删 & 文件压缩指令
9 0