【Linux篇】第十篇——基础IO(系统文件IO+文件描述符+重定向+文件系统+软硬链接)(一)

简介: 【Linux篇】第十篇——基础IO(系统文件IO+文件描述符+重定向+文件系统+软硬链接)

C语言文件IO介绍


文件操作库函数的简单使用


C语言中的文件操作函数如下:

文件操作函数 功能
fopen 打开文件
fclose 关闭文件
fputc

写入一个字符

fgetc 读取一个字符
fputs 写入一个字符串
fgets 读取一个字符串
fprintf

格式化写入数据

fcanf 格式化读取数据
fwrite 向二进制文件写入数据
fread 从二进制文件读取数据
fseek 设置文件指针的位置
ftell 计算当前文件指针相对于起始位置的偏移量
rewind 设置文件指针到文件的起始位置
ferror 判断文件操作过程中是否发生错误
feof 判断文件指针是否读取到文件末尾

先看一下C语言的两个库函数

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );// 写文件
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );// 读文件

fread: 第一个参数是从读取数据放到这,第二个参数是一次读入多少个字节的大小的数据,第三个参数是最多读几次,第四个参数是从这个流读数据。返回值是代表这次实际读的次数。只适用于文件流

fwrite: 第一个参数是从buffer获得数据,第二个参数是一次写入多少个字节的大小的数据,第三个参数是最多写几次,第四个参数是写数据到这个流。返回值是代表这次实际写的次数。只适用于文件流。

对文件进行写入操作示例:

#include<stdio.h>
#include<string.h>
int main()
{
  FILE* fp=fopen("log.txt","w");
  if(fp==NULL)
  {
    perror("open file fail!\n");
    exit(-1);
  }
  const char* msg="hello world!\n";
  int cnt=5;
  while(cnt--)
  {
    fwrite(msg,strlen(msg),l,fp);
  }
  fclose(fp);
  return 0;
}

运行结果:

image.png

对文件进行读取操作示例

#include <stdio.h>
#include <string.h>
int main()
{
  FILE* fp = fopen("log.txt", "r");
  if (fp == NULL){
    perror("open file fail");
    return 1;
  }
  char buf[256] = {0};
  int ret = 0;
  while ((ret = fread(buf, 1, 13, fp))){
    printf("%s", buf);
  }
  fclose(fp);
  return 0;
}

运行结果

image.png

stdin&stdout&stderr


Linux下一切皆文件,硬件设备也是被当做文件看待的,也就是说这些硬件设备也是可以通过IO打开的,并且进行读写。那他们是怎么操作的?一般来说,C语言程序运行起来,都会默认打开3个流,分别是:

  • stdin:标准输入流(键盘)
  • stdout:标准输出流(显示器)
  • stderr:标准错误流(显示器)

查看man手册我们就可以发现,stdin,stdout以及stderr这三个家伙实际上FILE*类型的。

extern FILE* stdin;
extern FILE* stdout;
extern FILE* stderr;

我们不需要考虑要打开键盘和屏幕这些流。这也是为什么我在打印数据到屏幕或从键盘上输入数据时,即使我们没有打开这些流,我们也可以执行这些操作的原因。


将文件操作中写文件的库函数传参进行更改,我们选择不传文件,而是传一个stdout,因为它的类型是FILE*,因此我们也可以把数据写到屏幕上。

#include <stdio.h>
#include <string.h>
int main()
{
  FILE* fp = fopen("log.txt", "w");
  if (fp == NULL){
    perror("open file fail");
    return -1;
  }
  const char* msg = "hello world!\n";
  int count = 5;
  while (count--){
    fwrite(msg, strlen(msg), 1, stdout);
  }
  fclose(fp);
  return 0;
}

运行结果如下所示:

image.png

总结:不止C语言当中有标准输入流,标准输出流和标准错误流,C++当中也有对应的cin,cout和cerr,其他语言当中都有类似的概念。实际上这种特性并不是某种语言所特有的,而是由操作系统所支持的。

系统文件I/O


操作系统除了C语言接口,C++接口或是其他语言接口外,操作系统也有一套系统接口来进行文件的访问。

相比于C库函数或其他语言的库函数而言,系统调用接口更贴近底层,实际上这些语言的库函数都是对系统接口进行了封装。

我们在Linux平台下运行C代码时,C库函数就是对Linux系统调用接口进行的封装,在Windows平台下运行C代码时,C库函数就是对Windows系统调用接口进行的封装,这样做使得语言有了跨平台性,也方便进行二次开发。

系统调用接口的介绍



open


作用:打开一个文件

函数原型:

1. int open(const char* pathname,int flags);
2. int open(const char* pathname,int flags,mode_t mode);

参数介绍:

  • pathname:要打开或创建的目标文件的路径名
  • flags:打开文件时,可以传入多个参数,用传入的参数进行或运算,得出flags
  • O_RDONLY:只读打开
  • O_WRONLY:只写打开
  • O_RDWR:读,写打开这三个常量,必须指定一个且只能指定一个
  • O_CREAT:若文件存在,则创建它。需要使用mode选项,来指明新文件的访问权限。
  • O_APPEND:追加写

说明:这里的每一个选项都只有一个比特位是为1的,其余都是0,所以将这些选项组合就是对这些选项进行或运算,然后传入flags.

  • mode:文件权限。在新文件被创建时,参数mode具体指明了使用权限。他通常也会被umask修改。所以一般新建文件的权限为(mode&~umask).
  • 返回值:
  • 成功:返回新的文件描述符
  • 失败:-1

实例演示:open函数的使用,研究函数的返回值

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
  int fd1 = open("log.txt1", O_RDONLY|O_CREAT, 0664);
  int fd2 = open("log.txt2", O_RDONLY|O_CREAT, 0664);
  int fd3 = open("log.txt3", O_RDONLY|O_CREAT, 0664);
  int fd4 = open("log.txt4", O_RDONLY|O_CREAT, 0664);
  int fd5 = open("log.txt5", O_RDONLY|O_CREAT, 0664);
  printf("fd1:%d\n", fd1);
  printf("fd2:%d\n", fd2);
  printf("fd3:%d\n", fd3);
  printf("fd4:%d\n", fd4);
  printf("fd5:%d\n", fd5);
  return 0;
}

运行结果如下:

image.png

观察运行结果,返回值fd是从3开始分配,且是递增的,大家看到这一串数字很容易想到数组下标。但0,1,2跑哪去了?其实在Linux下,进程会默认把3个文件描述符分配(0,1和2)给标准输入,标准输出和标准错误,所以,后序如果打开文件,文件描述符就是从3开始分配的。

close


作用:关闭文件

函数原型:

int close(int fd);

使用close函数传入需要关闭文件的文件描述符即可,若关闭文件成功则返回0,若关闭文件失败则返回-1。

write


函数原型:

ssize_t write(int fildes,const void* buf,size_t nbyte);

函数参数:

  • fd:在文件描述符为fd的文件中进行写入
  • buf:从buf位置开始读取数据
  • nbyte:从buf位置开始读取nbyte个字节到文件中
  • 函数返回值:
  • 成功:返回实际写入数据的字节数
  • 失败:返回-1

实例演示:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
  int fd = open("log.txt", O_WRONLY|O_CREAT, 0664);
  char buf[15] = "hello world\n";
  write(fd, buf, sizeof(buf)/sizeof(buf[0]));
  close(fd);
  return 0;
}

代码运行结果如下:

image.png

read


作用:读文件

函数原型:

ssize_t read(int fd,void* buf,size_t count);

函数参数:

  • fd:在文件描述符为fd的文件中开始读
  • buf:把读得内容从buf的位置开始存放
  • count:从buf位置开始存放count个字节

函数返回值:

  • 成功:返回实际读取数据的字节数
  • 失败;返回-1

实例演示:f

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
  int fd = open("log.txt", O_RDONLY);
    if (fd < 0){
    perror("open");
    return 1;
  }
  char ch;
  while (1){
    ssize_t s = read(fd, &ch, 1);
    if (s <= 0){
      break;
    }
    write(1, &ch, 1); //向文件描述符为1的文件写入数据,即向显示器写入数据
  }
  close(fd);
  return 0;
}

运行结果如下所示:

image.png

目录
打赏
0
0
0
0
15
分享
相关文章
Linux系统资源管理:多角度查看内存使用情况。
要知道,透过内存管理的窗口,我们可以洞察到Linux系统运行的真实身姿,如同解剖学家透过微观镜,洞察生命的奥秘。记住,不要惧怕那些高深的命令和参数,他们只是你掌握系统"魔法棒"的钥匙,熟练掌握后,你就可以骄傲地说:Linux,我来了!
67 27
|
8天前
|
Linux系统ext4磁盘扩容实践指南
这个过程就像是给你的房子建一个新的储物间。你需要先找到空地(创建新的分区),然后建造储物间(格式化为ext4文件系统),最后将储物间添加到你的房子中(将新的分区添加到文件系统中)。完成这些步骤后,你就有了一个更大的储物空间。
57 10
|
10天前
|
微服务2——MongoDB单机部署4——Linux系统中的安装启动和连接
本节主要介绍了在Linux系统中安装、启动和连接MongoDB的详细步骤。首先从官网下载MongoDB压缩包并解压至指定目录,接着创建数据和日志存储目录,并配置`mongod.conf`文件以设定日志路径、数据存储路径及绑定IP等参数。之后通过配置文件启动MongoDB服务,并使用`mongo`命令或Compass工具进行连接测试。此外,还提供了防火墙配置建议以及服务停止的两种方法:快速关闭(直接杀死进程)和标准关闭(通过客户端命令安全关闭)。最后补充了数据损坏时的修复操作,确保数据库的稳定运行。
41 0
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
378 12
Java IO 接口(Input)究竟隐藏着怎样的神秘用法?快来一探究竟,解锁高效编程新境界!
【8月更文挑战第22天】Java的输入输出(IO)操作至关重要,它支持从多种来源读取数据,如文件、网络等。常用输入流包括`FileInputStream`,适用于按字节读取文件;结合`BufferedInputStream`可提升读取效率。此外,通过`Socket`和相关输入流,还能实现网络数据读取。合理选用这些流能有效支持程序的数据处理需求。
100 2
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。
【IO面试题 一】、介绍一下Java中的IO流
Java中的IO流是对数据输入输出操作的抽象,分为输入流和输出流,字节流和字符流,节点流和处理流,提供了多种类支持不同数据源和操作,如文件流、数组流、管道流、字符串流、缓冲流、转换流、对象流、打印流、推回输入流和数据流等。
【IO面试题 一】、介绍一下Java中的IO流
Java零基础入门之IO流详解(二)
Java零基础入门之IO流详解(二)

热门文章

最新文章