Linux文件I/O操作(open,close,read,write,lseek)

Linux系统中,一切皆文件,对所有外部设备的操作,都可以抽象成对文件的读写。

Linux 文件IO

Linux中做文件IO最常用到的5个函数是: open , close , read , write 和 lseek ,这5个函数是不带缓冲的IO,也即每个read和write都调用了内核的一个系统调用。

下面对这5个函数进行介绍。

什么是文件描述符?

对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。通常0是标准输入,1是标准输出,2是标准错误。正是有了它们,你的简单程序才可以从控制台读入数据,输出日志,输出错误打印等等。

当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时。使用open或creat返回的文件描述符标识该文件。将其作为传送给read或write。

打开文件,获取文件描述符

函数原型

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char* pathname,int flags);
int open(const char* pathname,int flags,mode_t mode);

参数

flags

flags字段使用POSIX的几个宏,此时必须包含头文件<fcntl.h>才行。
可以是下面几个宏的逻辑或组合。这些宏共有三种类型:

访问方式 	     描述

O_RDONLY 	     只读
O_WRONLY 	     只写
O_RDWR 	         可读写
打开时标志 	      描述
O_CREAT 	      创建文件,需要指定第餐个参数mode
O_EXCL 	          与O_CREAT联用,如果文件已存在则返回错误
O_TRUNC 	      将清空文件的内容,仅对普通文件有用
O_NOCTTY 	      若打开的文件是终端设备,不让它作为该进程的控制终端
O_NOBLOCK 	      以非阻塞模式打开

读操作

函数原型

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

参数

参数 	描述
fd 	    文件描述符
buf 	读取的数据存放在buf指针指向的缓冲区
count 	读取的字节数

这里要说一下关于count:如果buf是一个字符数组名,那么count就用它的sizeof值。若buf是字符指针(字符串)则count用它的strlen值。

返回值:若果函数执行成功,返回读取的字节数,如果遇到EOF,则返回0。出错返回-1,并设置相应errno值。

写操作

函数原型

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

参数
参数同read函数。

参数 	描述
fd 	    文件描述符
buf 	读取的数据存放在buf指针指向的缓冲区
count 	读取的字节数

如果函数调用成功,返回值为写入的字节数;否则返回值为-1,并设置相应的errno值。

关闭文件

调用close函数即可,它的参数是前面打开的时候获得的文件描述符

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

成功返回0,失败则返回-1,并且会设置errno。

实例

以上理论太多,还是先看看一些简单的例子:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main(int argc, char const *argv[]) {

    int fd = -1; //文件描述符

    //打开文件
    fd = open( "myfile.txt", O_RDWR );

    if ( -1 == fd ) {

        //perror("open failed:");
        printf("open failed:%s\n", strerror(errno)); 
    }else {

        printf("open succeed=%d\n", fd );
    }

    //写文件
    char buf[] = "hello,程序猿编码";
    
    ssize_t count = write( fd, buf, sizeof(buf));
    if ( -1 == count ) {

        perror("write failed");
    }else {

        printf("文件写入成功,实际写入的字节数目为:%d\n", count);
    }

    //关闭文件
    close( fd );

    return 0;
}

编译运行
在这里插入图片描述
当open返回-1(很多系统接口类似)时,就会设置errno,这个时候就可以调用perror接口打印对应的错误信息。write也是类似这个意思。这样便于我们定位问题。即:

perror("open failed:");
printf("open failed:%s\n", strerror(errno)); 

上面两种方式都可以打印出错误信息,区别在于,前者输出到标准错误,后者输出到标准输出。

标准错误是无缓冲的,可以参考《不可不知的Linux中三种缓冲模式

打开一个存在的文件

在O_RDWR模式下,对于一个已经存在的文件,且有内容,那么写入文件会覆盖对应大小的源文件内容【不是完全覆盖】

int fd = open( "test.txt", O_RDWR );

打开一个文件,不存在时创建

既然不存在时,会打开失败,那么不存在就创建好了,这就用到了O_CREATE标志。因此修改open函数那一行:

int fd = open("test.txt",O_WRONLY | O_CREAT);

注意到了吗,多个标志使用|构成flags参数。

打开一个文件,存在时截断

前面已经实现了文件不存在时,创建,存在时也可以正常打开,如果存在时,又不想要原先的内容?那就需要用到O_TRUNC标志。

int fd = open("test.txt",O_WRONLY | O_CREAT | O_TRUNC);

现在假设test.txt文件存在,且里面有内容,再次运行后,发现打开文件正常,且内容只有新加入的,而没有之前存在的。

在打开的文件后追加内容

如果想在打开的文件后追加内容,那么可以使用O_APPEND标志

int fd = open("test.txt",O_WRONLY | O_CREAT | O_APPEND);

这样如果原来test.txt中有内容,则可以往文件中追加内容。

lseek

功能:移动打开的文件的读写指针的位置。

函数原型:

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

参数:

fd是文件描述符
offset是偏移量
whence是偏移量的基准位置。它的取值有三个:
SEEK_SET: 开始位置
SEEK_CUR: 当前位置
SEEK_END: 末尾位置

为什么开始位置的后缀是_SET?

实际上,在man手册中可以看出。这三个宏的描述是
SEEK_SET The offset is set to offset bytes.
SEEK_CUR The offset is set to its current location plus offset bytes.
SEEK_END The offset is set to the size of the file plus offset bytes.

fd的偏移量

在内核中对一个文件描述符(fd)的偏移量只维护一个值,也就是说你用读写方式打开一个文件,如果先用read读取了n个字符,紧接着用write写入了n个字符,那么后来写入的n个字符并不是从文件第一个字符位置开始的,而是从n+1个字符位置开始的。所以通常我们需要使用lseek来使fd的偏移量置于文件开始位置。

简单应用:统计文件大小。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>


int main(int argc, char const *argv[]) {

    if ( argc != 2 ) {

        printf("usage:%s %s\n", argv[0], "filename");
        return -1;
    }

    int fd = -1;

    fd = open( argv[1], O_RDWR );

    if( -1 == fd ) {
        printf("文件打开失败,错误号:%d\n", errno );
        perror( "open" );
        return -1;
    }else {
        printf("文件打开成功\n");
    }

    //把指针移动到文件末尾,就是文件的大小
    int count = lseek( fd, 0, SEEK_END );

    printf("文件大小为: %d\n", count);

    close( fd );
    return 0;
}

编译运行
在这里插入图片描述

总结

本文对文件I/O只做打开,创建,读写,偏移量操作。虽然本文的I/O函数不带缓冲,但是读写时,一定要选择合适的buf大小是非常关键。要善于参考man手册。

在这里插入图片描述

欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料。进入技术交流群。网盘资料有如下:

在这里插入图片描述

发布了131 篇原创文章 · 获赞 115 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/chen1415886044/article/details/105335994