【Linux】基础IO_文件操作

简介: 【Linux】基础IO_文件操作

预备知识



在C语言阶段,我们学习过相关文件操作,但是,我们真的了解文件操作吗?


答案是并不了解,因为我们之前所学习的C语言文件操作,都是属于语言层面的操作,我们只是知道了fopen,fclose这些函数是怎么用,但是具体的底层原理并没有涉及接触。实际上,当涉及到文件操作的原理时,就不再是简单的语言问题,而是属于系统问题。


1.png


是否只有我们的C/C++才可以对文件进行操作呢?


答案也是否定的,其它比如JAVA、Python等语言也支持相关文件操作,只是不同语言的实现方法(语言级别)不同罢了,但是底层(系统级别)都会涉及到系统层面的相关操作。

如何看待文件?


文件=内容+属性,对文件的相关操作--->对内容/属性的相关操作

文件存放在磁盘中,只有当我们打开文件进行相关操作时,文件的属性会被加载到内存(冯诺伊曼体系结构决定,CPU只与内存交互)

对于大量被打开的文件,OS会进行管理,OS会创建struct file结构体,该结构体内包含对应文件的属性(先描述),然后通过某种数据结构,将各个文件对应的结构体连接起来(再组织)。对被打开文件的管理--->对该数据结构的增删查改。

文件是怎么打开的?由谁打开?


由用户通过创建进程(写代码,编译运行),底层会通过系统调用,从而让OS打开文件。

本章所讨论的文件操作都是针对被打开的文件。


文件操作



语言级别的文件操作(C)


文件的打开

FILE *fopen(const char *path, const char *mode);


对于该函数path表示打开或创建的目标文件(默认会在当前路径下创建/打开),mode表示文件的打开方式。对于mode来说,这里就简单介绍以下几种(更多的在前文:点击跳转):


image.png


FILE* fp1=fopen("./TEST/log.txt", "w");//只写,文件不存在在,在./TEST/下创建该文件
FILE* fp2=fopen("log.txt","a");//追加,文件不存在,在当前路径下创建该文件
FILE* fp3=fopen("log.txt","r");//只读,同一个文件可以被打开多次


文件的关闭


int fclose(FILE *fp);//对已打开的文件进行关闭。

对于一个被打开的文件,最后一定要关闭该文件。这就好比我们malloc出来的空间一定要free一样。fclose与fopen配套使用。


fclose(fp1);  
fclose(fp2);  
fclose(fp3);


文件的写入


C语言提供的写函数一共有以下几种:


image.png


对于上面的大多数函数,这里就不做过多讲解,可以自行前去观看,这里讲一下snprintf。snprintf是fprintf的优化版本,相较于fprintf,可以对写入数据进行长度控制,会更加安全。


int snprintf(char *str, size_t size, const char *format, ...);

str:表示要写入的缓冲区的地址

size:表示该缓冲区的大小

format:格式化可变参数(比如:"%d:%s\n",x,y )演示代码如下:

#include<stdio.h>
 #include<stdlib.h>
 #define SIZE 128
 int main()
 {
   FILE* fp=fopen("log.txt","w");//写
   //打开失败返回空
   if(fp==NULL)
   {
     perror("fopen fail\n");
     exit(-1);
   }
   char buffer[SIZE];
   int cnt=10;
   const char* st="hello world!";
   while(cnt)
   {
     //格式化写入到buffer数组
     snprintf(buffer,sizeof(buffer),"%d:%s\n",cnt,st);
     //再将数组内容写到文件                                                                                                                    
     fputs(buffer,fp);
     --cnt;
   }
   //关闭文件
   fclose(fp);
   return 0;
 }


2.png


文件的读取


对于文件的读取,C提供的函数与上面一一对应,用法也是如此


文件读取演示:


#include<stdio.h>
 #include<stdlib.h>
 #include<string.h>
 #define SIZE 128

 

//文件的读取
 int main()
 {
   FILE* fp=fopen("log.txt","r");//读方式打开
   if(fp == NULL)
   {
     perror("fopen fail\n");
     exit(-1);
   }
   //按行来读取文件
   while(1)
   {
     //从文件流中按行读取内容,读到tmp中
     char tmp[SIZE];
     if(fgets(tmp,sizeof(tmp),fp)==NULL)
     {
       break;
     }
     //打印tmp
     printf("%s",tmp);                                                                                                                         
   }
   //关闭文件
   fclose(fp);
   return 0;
 }


3.png


系统级别的文件操作


以上都是语言级别的文件相关操作。至此往下将讲解系统级别的相关文件操作。


文件的打开


对于文件的打开,我们采用open系统调用函数。如下:


//头文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//系统调用函数open

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

对于该函数,有如下理解:


返回值:打开成功,返回一个int类型的整数->文件描述符(下一章将重点讲解),失败返回-1。

参数pathname:打开或创建的目标文件(默认在当前路径打开/创建)。

参数flags:多参数选项,用一个整形来实现多个选项的传递。以下为flags对应的参数选项,通常用或位运算来表示一个或多个参数。

对于多参数选项flags:


1682435943710.png


多参数传递实际上采用的就是位图的方式来实现,一个整形一共32个比特位,每一个比特位都可以用来表示一个参数,用|运算符则可以实现一个整形传递多个参数选项。


4.png


我们可以利用此特点来自己实现一个小demo,如下图所示:


5.png


参数flags便是利用此特点来实现一个整形多个参数选项。与我们的C语言对应关系如下;


6.png


参数mode:创建文件时给文件一个权限


文件的关闭


通过系统调用函数close来关闭一个文件,该函数如下:


#include <unistd.h>
int close(int fd);

其中这里的fd就是文件描述符,也就是open函数返回的那个整形。具体含义将在下一篇详细讲解。


文件写入


通过函数write来实现文件的写入操作


#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

对于该函数:


返回值:写入成功,会返回写入的内容的大小(单位:字节)失败返回-1

参数fd:文件描述符即open函数的返回值

参数buf:缓冲区的地址

参数count:表示写入的文件的大小

具体案例操作:


#include<stdio.h>
 #include<stdlib.h>
 #include<string.h>
 #include<unistd.h>
 #include<sys/types.h>
 #include<sys/stat.h>
 #include<fcntl.h>
 #define SIZE 128
 int main()
 {
 umask(0);//设置umask为0
   int fd=open("log.txt",O_CREAT|O_WRONLY|O_TRUNC,0666);//以写的方式打开文件,文件不存在则创建,打开之前会清空原文件,创建时文件权限为0666

 

//打开失败
   if(fd == -1)
   {
     perror("open fail\n");
     exit(-1);
   }
   char buffer[SIZE];//充当缓冲区的buffer数组
   const char* str="hello world";
   int cnt=10;
   //利用snprintf进行格式化写入到缓冲区,再写入到文件
   while(cnt)                                                                                                                                  
   {
     snprintf(buffer,sizeof(buffer),"%d:%s\n",cnt,str);
     --cnt;
     ssize_t n=write(fd,buffer,strlen(buffer));//这里我们不需要将/0计算在内,因为/0是C语言规定的字符串结尾,此时我们处于系统级别
     printf("成功写入%lu个字节内容\n",n);//%lu:无符号整型
     //间隔1秒写入一次                                                                                                                        
    sleep(1);
   }
   //关闭文件
   close(fd);
   return 0;
 }


运行结果如下:


7.gif


文件的读取


对于文件的读取,提供了函数read


#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

对于该函数:


返回值:读取成功,返回读取的内容的大小,失败返回-1

参数fd:文件描述符

参数buf:缓冲区地址,用来存放读到的数据

count:一次读取字符的大小到缓冲区buf

具体操作如下:


#include<stdio.h>
 #include<stdlib.h>
 #include<string.h>
 #include<unistd.h>
 #include<sys/types.h>
 #include<sys/stat.h>
 #include<fcntl.h>
 #define SIZE 128

//文件读取

int main()
 {
   int fd=open("log.txt",O_RDONLY);//读方式打开文件
   if(fd== -1)
   {
     perror("open fail\n");
     exit(-1);                                                                                                                                 
   }
   char buffer[SIZE];//读到的内容放进buffer数组
   //注意处理\0
   ssize_t n=read(fd,buffer,sizeof(buffer)-1);
   //读取成功
   if(n>0)
   {
     buffer[n]='\0';
     printf("%s共读取%lu字节的内容\n",buffer,n);//字符串结尾的标志\0(这里我们用printf打印,属于C语言级别,所以得考虑\0的存在)
   }
   //关闭文件
   close(fd);
   return 0;
 }


运行结果如下:


8.gif


语言操作与系统级别的文件操作区别


实际上,我们所有的语言级别的文件操作都是对系统调用的封装,比如我们使用的fclose,fopen等函数,他们的底层实际上都会调用对应的系统级别的函数,比如close、open等。也就是说,语言级别的文件操作的底层,并不会直接跳过OS,而是一层一层往下进行。


9.png



相关文章
|
2月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
101 0
|
9天前
|
Ubuntu Linux Shell
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
(已成功解决)Linux环境报错—bash: wget: command not found;常见Linux发行版本,Linux中yum、rpm、apt-get、wget的区别;Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
146 68
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
|
2月前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
117 1
Linux C/C++之IO多路复用(aio)
|
1天前
|
Linux API C语言
Linux基础IO
Linux基础IO操作是系统管理和开发的基本技能。通过掌握文件描述符、重定向与管道、性能分析工具、文件系统操作以及网络IO命令等内容,可以更高效地进行系统操作和脚本编写。希望本文提供的知识和示例能帮助读者更深入地理解和运用Linux IO操作。
25 14
|
4月前
|
缓存 安全 Linux
Linux 五种IO模型
Linux 五种IO模型
|
1月前
|
运维 监控 网络协议
运维工程师日常工作中最常用的20个Linux命令,涵盖文件操作、目录管理、权限设置、系统监控等方面
本文介绍了运维工程师日常工作中最常用的20个Linux命令,涵盖文件操作、目录管理、权限设置、系统监控等方面,旨在帮助读者提高工作效率。从基本的文件查看与编辑,到高级的网络配置与安全管理,这些命令是运维工作中的必备工具。
145 3
|
2月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
41 0
Linux C/C++之IO多路复用(poll,epoll)
|
3月前
|
人工智能 监控 Shell
常用的 55 个 Linux Shell 脚本(包括基础案例、文件操作、实用工具、图形化、sed、gawk)
这篇文章提供了55个常用的Linux Shell脚本实例,涵盖基础案例、文件操作、实用工具、图形化界面及sed、gawk的使用。
799 2
|
4月前
|
Linux 数据安全/隐私保护 Perl
解锁Linux高手秘籍:文件操作+命令解析大揭秘,面试场上让你光芒万丈,技术实力惊艳四座!
【8月更文挑战第5天】Linux作为服务器与嵌入式系统的基石,其文件管理和命令行操作是技术人员必备技能。本文从文件操作和基础命令两大方面,深入浅出地解析Linux核心要义,助你在面试中脱颖而出。首先探索文件系统的树状结构及操作,包括使用`ls -la`浏览文件详情、`touch`创建文件、`rm -r`慎删目录、`cp`与`mv`复制移动文件、以及利用`find`搜索文件。接着掌握命令行技巧,如用`cat`、`more`和`less`查看文件内容;借助`grep`、`sed`与`awk`处理文本;运用`ps`、`top`和`kill`管理进程;并通过`chmod`和`chown`管理文件权限。
85 8
|
4月前
|
小程序 Linux 开发者
Linux之缓冲区与C库IO函数简单模拟
通过上述编程实例,可以对Linux系统中缓冲区和C库IO函数如何提高文件读写效率有了一个基本的了解。开发者需要根据应用程序的具体需求来选择合适的IO策略。
39 0