Linux系统下的文件I/O操作函数

  1. 序言
  2. open函数
  • open函数的使用介绍
  • open函数的返回值
  • open函数的使用示例
  1. read函数
  • read函数的使用介绍
  • read函数的返回值
  • read函数的使用示例
  1. write函数
  • write函数的使用介绍
  • write函数的返回值
  • write函数的使用示例
  1. close函数
  • close函数的使用介绍
  • close函数的返回值
  • close函数的使用示例
  1. lseek函数
  • lseek函数的使用介绍
  • lseek函数的返回值
  • lseek函数的使用示例
    7.实战示例

1.序言

该文章主要是讲如何在Linux系统对文件进行操作,毕竟Linux系统下都是以文件的形式存在,那么我们就来看看在linux系统下是如何对文件进行一些简单的操作。

2 打开文件 open函数

open函数的使用介绍

open即为打开,在linux中可以通过man命令来查看open函数详情

在这里插入图片描述

图1

从图1中可以知道,使用open函数,必须要包含sys/types.h、sys/stat.h、fcntl.h头文件,以及最基本的两种使用方式。可以看出第一种和第二种的使用方式区别在于参数个数不一样,因为open函数是可变参函数,那么接下来介绍一些参数的含义。

pathname:用于标识被打开或者新建的文件,属于字符串类型,可以是相对路径和绝对路径,也可是符号链接进行解引用。

flags:使用open函数时一个标志,整形类型,但是这些类型都已经被宏定义,直接使用宏定义名即可。

    O_RDONLY  :以只读方式打开文件,被开的文件只能进行读操作,进行其它操作则会失败。
    O_WRONLY  :以只写方式打开文件,被开的文件只能进行写操作,进行其它操作则会失败。
    O_RDWR    :以可读可写方式打开文件,被开的文件可以进行读写操作。
    O_CREAT   :若是pathname参数的文件不存在则会创建以参数pathname路径下的文件名命名的新文件。
    O_NOFOLLOW:若是pathname参数所指向是符号链接,则不会进行解引用,返回错误。
    
    上述是比较常用标志,当然open函数flags参数标志也远不止于这几个。参数之间也可通过或运行(|)进行组合。
    例如:打开一个当前目录下home目录下不存在的aaa.c文件,可对文件进行可读写操作
     open( "./home/aaa.c", O_RDWR | O_CREAT );

mode:made参数是用于文件所属用户、同组用户以及其他用户对新建文件的访问权限,所以,只有flags参数中含有含 O_CREAT 或 O_TMPFILE 标志时才会有效,mode_t是一个u32的无符号类型,同样的,直接使用宏定义的名称设置该参数就行了,下面是made参数的宏定义名称介绍。

    S_ISUID:设置用户ID(特殊权限)
    S_ISGID:设置组用户ID(特殊权限)
    S_ISVTX:保存使用后交换的文本sticky(特殊权限),S_ISVTX即为黏着位,如果对目录设置了这一位,则只有对目录有写权限的用户,并且有(拥有文件、拥有目录、超级用户)权限之一才能对目录下文件删除或重命名
    S_IRUSR:允许文件所属者读文件
    S_IWUSR:允许文件所属者写文件
    S_IXUSR:允许文件所属者执行文件
    S_IRWXU:允许文件所属者读、写、执行文件
    S_IRGRP:允许同组用户读文件
    S_IWGRP:允许同组用户写文件
    S_IXGRP:允许同组用户执行文件
    S_IRWXG:允许同组用户读、写、执行文件
    S_IROTH:允许其他用户读文件
    S_IWOTH:允许其他用户写文件
    S_IXOTH:允许其他用户执行文件
    S_IRWXO:允许其他用户读、写、执行文件
    
    上述就是made参数的一些宏定义,也是同运行(|)进行组合。
    例如:打开一个当前目录下home目录下不存在的bbb.c文件,使用对文件可读写的方式打开,允许文件所有者可读、可写、可执行,允许同组用户可读。
    open( "./home/bbb.c", O_RDWR | O_CREAT, S_IRWXU | S_IRGRP );

open函数的返回值

使用open函数时,返回值一般会有两种结果,失败返回-1,成功将会返回一个非负整数的文件描述符;对于 Linux 内核而言,所有打开的文件都会通过文件描述符进行索引。在一个进程内打开的文件大小是有限制的,最大值默认为1024,一般来说,第一个成功打开文件的文件描述符是0,接着第二个文件是1,第三个文件是2……,以此类推。
不过文件描述符的前三位已经分配给系统使用(0系统标准输入,1系统标准输出,2标准错误),一般都是从3开始。

open函数的使用示例

1.以只读方式打开一个在当前目录下已经存在的文件abc.c
fd = open( "./abc.c", O_RDONLY );
if( -1 == fd)
{
    return fd;
}

2.以可读可写的方式打开一个在当前目录下hoem目录下指定的文件bac.c,如果是一个符号链接文件,则不对其进行解引用,返回错误
fd = open( "./hoem/bac.c", O_RDWR | O_NOFOLLOW );
if( -1 == fd)
{
    return fd;
}

3.打开一个在当前目录home目录下不存在的文件名cba.c,使用对文件可读写的方式打开,允许文件所有者可读、可写、可执行,允许同组用户可写,其它用户可读。
fd = open( "./home/bbb.c", O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH );
if( -1 == fd)
{
    return fd;
}

3 读文件 read函数

read函数的使用介绍

read即为读,在linux中通过man命令来查看read函数详情如下图2所示

在这里插入图片描述

图2

从图2中可以知道,使用read函数,必须要包含unistd.h头文件,而read函数只有一种原型三个参数。

fd:整形类型,文件描述符,在open函数中已经介绍过了,即为open函数的返回值。

buf:用于存储数据的缓冲区。

count:指定需要读取的字节数

read函数的返回值

read返回值是如果读成功则返回当前读取到的字节数,实际读取的字节数不一定等于count指定的字节数,可能小于count指定的字节数或者为0,因为不管是读操作还是写操作,都要清楚文件从那里开始读,那就是关于位置偏移量的问题了,默认情况下当前位置偏移量一般为0,也就是文件的起始位置,当使用read、write操作后,当前位置偏移量也会向后移动相应的字节数,比如当前位置偏移量为0,使用read函数读取500字节之后,那么读取后的位置偏移量会移动到500个字节数。因此,如果当前位置偏移量后的字节数小于需要读取的字节数或者已经是文件末尾了,实际读取的字节数可能会小于count指定的字节数或者为0,位置偏移量可以通过 lseek 系统调用进行设置。

read函数的使用示例

1.以只读方式打开一个在当前目录下已经存在的文件abc.c,读取1kb的数据在buf中。
char buff[1024];
int fd,ret;
fd = open( "./abc.c", O_RDONLY );
if( -1 == fd)
{
    return fd;
}
ret = read(fd, buff, sizeof(buff));//read(文件描述符,buff缓冲区的首地址,读取指定的字节数)
if (-1 == ret)
{
    close(fd);
}

4 写文件 write函数

write函数的使用介绍

write即为写,在linux中通过man命令来查看write函数详情如下图3所示

在这里插入图片描述

图3

从图3中可以知道,使用write函数,也必须要包含unistd.h头文件,write函数也是只有一种原型三个参数。

fd:整形类型,文件描述符,与read函数的fd参数意义相同

buf:用于存储数据的缓冲区。

count:指定需要读取的字节数

write函数的返回值

write返回值是如果写成功则返回当前写入的字节数,实际写入的字节数不一定等于count指定的字节数,可能小于count指定的字节数或者为0,例如磁盘空间不足以你要写入的字节数,0则表示未写入任何字节,写入出错则返回-1。

write函数的使用示例

1.以只写方式打开一个在当前目录下已经存在的文件abc.c,写入1Kb的数据在buf中。
char buff[1024] = {0};
int fd,ret;
fd = open( "./abc.c", O_WRONLY );
if( -1 == fd)
{
    return fd;
}
ret = write(fd, buff, sizeof(buff)); //write(文件描述符,buff缓冲区的首地址,写入指定的字节数)
if (-1 == ret)
{
    close(fd);
}

5 关闭文件 close函数

close函数的使用介绍

close即为关闭,在linux中通过man命令来查看close函数详情如下图4所示

在这里插入图片描述

图4

从图4中可以知道,使用close函数要包含unistd.h头文件,close函数只需要一个参数,即文件描述符。

fd:文件描述符,需要关闭的文件所对应的文件描述符。

close函数的返回值

如果成功返回 0,如果失败则返回-1。除了使用close可以关闭文件,当一个进程被中止时,系统内核会关闭该进程打开的所有文件,但是,一个良好的编程习惯,应该在不需要的时候就应该被关闭,这样代码的可读性、可靠性才会更好,且文件描述符是有限的,不使用时应该关闭释放,将文件描述符归还系统。

close函数的使用示例

1.以只读方式打开一个在当前目录下已经存在的文件abc.c,然后关闭。
int fd;
fd = open( "./abc.c", O_RDONLY );
if( -1 == fd)
{
    return fd;
}
close(fd);

6 lseek函数

lseek函数的使用介绍

在linux中通过man命令来查看lseek函数详情如下图4所示

在这里插入图片描述

图5

从图5中可以知道,使用lseek函数要包含sys/types.h、unistd.h头文件,lseek函数有三个参数,参数含义如下。

fd:文件描述符。

offset:位置偏移量,以字节为单位。

whence:用于定义参数 offset 偏移量对应的参考值,整形类型,已宏定义名称,详情如下。

SEEK_SET:读写的位置偏移量将指向从文件头开始 + offset 字节位置处;
SEEK_CUR:读写的位置偏移量将指向当前位置偏移量 + offset 字节位置处
SEEK_END:读写的位置偏移量将指向文件末尾 + offset 字节位置处,
注:在使用SEEK_CUR、SEEK_END时,offset 可以是正、也可以是负,是正数表示往后偏移、是负数则表示往前偏移。

lseek函数的返回值

如果lseek函数位置偏移量设置成功,则返回值成功将返回当前位置偏移量(从头部开始计算,单位为字节);设置发送错误将返回-1。

lseek函数的使用示例

1.以只读方式打开一个在当前目录下已经存在的文件abc.c,然后将当前读写的位置移动到开头的50个字节处。
int fd,ret;
fd = open( "./abc.c", O_RDONLY );
if( -1 == fd )
{
    return fd;
}
ret = lseek(fd, 50, SEEK_SET)
if( -1 == ret )
{
    return ret;
}

2.继续上面的操作,将当前读写的位置移动到上面操作后位置前的20个字节处。
ret = lseek(fd, -20, SEEK_CUR)
if( -1 == ret )
{
    return ret;
}

7 实战示例

1.以只读方式打开一个当前目录home目录下的已有文件a.c,从文件开头偏移200个字节处开始读取2Kb的数据到buff缓冲区内;
纸 再以只写方式创建一个当前目录home目录下的不存在的文件b.c,将buff缓冲区的2Kb数据写入b.c;
b.c的文件权限:所属用户可读可写可执行,同组用户可读可写,其它用户只可读。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
    char buff[2048] = {0};
    int fd1,fd2;
    int ret,ret1,ret2;

    fd1 = open( "./home/a.c", O_RDONLY ); //打开a.c文件
    if( -1 == fd1 )
    {
        return -1;
    }
    fd2 = open( "./home/b.c", O_WRONLY | O_CREAT, S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH ); //创建b.c文件
    if( -1 == fd2 )
    {
        return -1;
    }

    ret = lseek(fd, 200, SEEK_SET)
    if( -1 == ret )
    {
        close(fd1);                       //操作失败要释放文件描述符
        close(fd2);
        return ret;
    }
    
    ret1 = read( fd1, buff, sizeof(buff) ); //从a.c读数据
    if( -1 == ret1 )
    {
        close(fd1);                       //操作失败也要释放文件描述符
        close(fd2);
        return ret1
    }
    else
    {
        close(fd1);                        //读成功后释放文件描述符
    }

    ret2 = wirte( fd2, buff, sizeof(buff) ); //往b.c写数据
    if( -1 == ret1 )
    {                       
        close(fd2);                       //操作失败也要释放文件描述符
        return ret1
    }
    else                                    
    {
        close(fd2);                       //写成功后释放文件描述符
    }
    
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勉扣王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值