最近更忙一些,所以更新频率降低了一些。
今天主要分享的是Linux中的文件IO,所谓IO,也就是输入输出,也就是文件的读和写。主要涉及到文件的打开,读写和关闭。
先说一些编译环境。因为现在讲的是Linux,所以最好是在Linux环境中来编译代码,虽然现在写的这些代码在Windows中也照样能够执行,效果也一样,但是最好还是养成在Linux中编译的习惯,后面更复杂一些的代码可能在两种环境中是不一样的。
要搭建Linux开发环境,通常的做法是安装虚拟机,然后在虚拟机当中安装Linux操作系统,这也是比较普遍的做法。我一开始也是这样做的,但是我的虚拟机有一点问题,即使安装了VMware tools,也无法在Windows与Linux中相互复制粘贴,更为要命的是共享文件夹后来不知道为什么无法使用了,网上的方法都不太行。这两个问题是非常致命的,因为这样子的话就没办法在Windows下写好代码,而必须用vim编辑器写代码,vim编辑器使用体验是很差的,所以必须要有新的解决办法。后来我在Windows下的cmd终端安装了gcc编译器,可以实现代码的编译,只不过生成的代码是.exe类型的,而不是Linux下的.out类型的,当然这个问题也不是很大,最大的问题是Windows下的cmd不能使用man手册,这就导致如果你想查看一个库函数的原型非常麻烦。
于是我今天又发现原来Windows可以在开发者模式下安装Linux子系统,只要去Microsoft store下载一个Ubuntu即可。这个子系统给我的体验还是非常不错的,首先,它解决了复制粘贴的问题,在命令行下,选中即复制,右键即粘贴,非常方便,也可以把Windows中的内容复制到命令行下。另外,它可以进入到电脑中任意的盘里面,解决了虚拟机共享文件夹的问题。下面给出一个截图,具体的安装方法可以自行百度。
可以看到,它和Linux下的命令行操作都是一样的。比虚拟机启动快多了,使用起来还是非常方便的。
好了,上面的内容都是环境的搭建,如果习惯使用虚拟机也没问题,这里只不过提供了另外一种不错的方式。
言归正传,文件操作主要涉及打开、读、写、关闭,还有一些不常用的这里就不介绍了,接下来逐个来分析一下。
1、打开文件
打开文件使用open函数,它的函数原型是
代码语言:javascript复制int open(const char*pathname, int flags);
int open(const char*pathname, int flags, mode_t mode);
这里再顺便说一下,就是如果我们不熟悉某个库函数的原型是什么样的,可以使用man手册来查询,man 1 xx查linuxshell命令,man 2 xxx查API, man 3 xxx查库函数
这个在前面的文章中都有说过。可以回顾一下:Linux笔记(1)| 常用命令
简单说一下这个函数,它的第一个参数是文件名,第二个参数是以什么样的方式来打开。
Flags可以填入:
O_RDONLY(只读)、 O_WRONLY(只写)、 O_RDWR(可读可写)
另外还有O_APPEND、O_TRUNC。O_APPEND属性就是说,如果你打开一个原本有内容的文件,你再往里面写内容是写在原来内容的后面,而O_TRUNC属性就是把原来内容清空后再写入。如果两个属性一起写,表现的是O_TRUNC属性,也即清空后再写。如果两个都不写,那么新内容会从头开始把原来的内容覆盖,没有覆盖完的就不变。可以看一下这部分代码:
如果使用open打开一个不存在的文件会怎么样呢?答案是会报错。如果想要创建并打开一个文件,就可以再加上O_CREAT,加上这个属性之后,就可以打开一个不存在的文件。如果加上这个属性又去打开一个存在的文件呢?那么里面的内容会被清空。所以这样会造成一个隐患,就是加上了这个属性,但是不小心打开了一个不是你想打开的文件,那么就会造成那个文件丢失。所以为了解决这个问题,通常让O_CREAT和O_EXCL一起使用,这样当你打开一个已经存在的文件的时候,它就会提示你File exists,避免不小心把已经存在的文件清空。
以上就是open的几个属性。另外,在使用了O_CREAT这个属性之后,还可以有第三个参数mode来指定要创建的文件的权限。mode使用4个数字来指定权限的,其中后面三个很重要,对应我们要创建的这个文件的权限标志。譬如一般创建一个可读可写不可执行的文件就用0666。
2、对文件进行写操作
Write函数的原型是
代码语言:javascript复制ssize_t write(int fd, constvoid *buf, size_t count);
第一个参数是文件描述符,文件描述符可以简单理解是区分文件的标志,比如你打开多个文件可以通过文件描述符来区分。第二个参数是要写入的内容,是一个指针。第三个是要写入的字节数。返回值是实际写入的字节数。如果写入失败会返回-1.这个比较容易。
3、读出文件内容
函数原型:
代码语言:javascript复制ssize_t read(int fd, void*buf, size_t count);
与write函数相似,这里也不多说
4、关闭文件
代码语言:javascript复制int close(int fd);
输入参数只有一个文件描述符,返回值如果为负表示关闭失败。
下面写一个简单的程序来概括内容
代码语言:javascript复制#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
char write_buff[]="linux";
char read_buff[50]={0};
void main(void)
{
//打开文件
int fd=-1;
int ret;
char *pread=NULL;
fd=open("a.txt",O_RDWR|O_CREAT|O_TRUNC|O_EXCL);
if(fd<0)
{
//printf("打开文件失败n");
//return;
perror("打开文件失败");
exit(-1);
}
printf("打开文件成功n");
//写入内容
ret=write(fd,write_buff,sizeof(write_buff));
if(ret<0)
{
//printf("写入失败n");
//return;
perror("写入失败");
exit(-1);
}
printf("写入成功n");
printf("实际写入了%d个字节n",ret);
//读出内容
lseek(fd,-sizeof(write_buff), SEEK_CUR);
ret=read(fd,read_buff,sizeof(write_buff));
if(ret<0)
{
//printf("读取失败n");
//return;
perror("读取失败");
exit(-1);
}
pread=read_buff;
printf("读取成功n");
printf("读取了%d个字节n",ret);
printf("读取的内容是");
while(*pread !='