P4 Linux 文件打开,关闭,读写,增删(open,close,read,write)

目录

一 文件描述符的概念

二 打开文件 open()、关闭文件 close()

1.创建(creat)

2.打开(open)

2.1 mod参数选择

2.2 flags:文件权限

3.open 函数使用示例

4. 关闭(close)

三 写文件 write()、读文件 read()

3.1 write 写文件

3.2 read 读文件

四 文件读写位置偏移量。

4.1 lseek

4.2使用示例:

五:案例练习

题目1

题目2

题目3

题目4


前言

                            

🎬 个人主页:@ChenPi

🐻推荐专栏1: 《C++_@ChenPi的博客-CSDN博客》✨✨✨ 

🔥 推荐专栏2: 《Linux C应用编程(概念类)_@ChenPi的博客-CSDN博客》✨✨✨

🛸推荐专栏3: ​​​​​​《链表_@ChenPi的博客-CSDN博客 》 ✨✨✨
🌺本篇简介  :  本期我们学习一下关于文件操作的系统调用,Linux一切皆文件,所以这章还是比较重要的,这章主要搞定三个API,open,read,write

一 文件描述符的概念

/* 打开源文件 src_file(只读方式) */ 
fd1 = open("./src_file", O_RDONLY);
/* 打开目标文件 dest_file(只写方式) */ 
 fd2 = open("./dest_file", O_WRONLY); 
  • 当调用 open 函数打开一个现有文件或创建一个新文件时,内核会向进程返回一个文件描述符,用于指 代被打开的文件。
  • 所有执行 IO 操作的系统调用都是通过文件描述符来索引到对应的文件,当调用 read/write 函数进行文件读写时,会将文件描述符传送给 read/write 函数
  • 所以在代码中,fd1 就是源文件 src_file 被打开时所对应的文件描述符,而 fd2 则是目标文件 dest_file 被打开时所对应的文 件描述符。
  1. 什么是文件描述符?     文件句柄、一个非负整数、与对应的文件相绑定
  2. 文件描述符如何分配     每次分配一个没有被使用的最小非负整数作为文件描述符
  3. 标准输入、标准输出和标准错误     0、1、2

一个进程可以打开多个文件,但是在 Linux 系统中,一个进程可以打开的文件数是有限制,并不是可以 无限制打开很多的文件,我们可以通过 ulimit 命令来查看进 程可打开的最大文件数,用法如下所示: ulimit -n

该最大值默认情况下是 1024,也就意味着一个进程最多可以打开 1024 个文件

二 打开文件 open()、关闭文件 close()

1.创建(creat)

函数原型:

int creat(const char *pathname,mode_t mode);  //pathname:指向文件路径的指针

int creat("/home/abl/demo",S_IRWXU);//绝对路径:/home/abl/demo

int creat("./demo",S_IRWXU);//当前路径:./demo

mode:创建模式(可读可写可执行)系统自带四个宏

  • 可执行(1):S_IXUSR
  • 可写(2):S_IWUSRL
  • 可读(4):S_IRUSR
  • 可读、可写、可执行(7):S_IRWXU

返回值为文件描述符

成功返回一个非负整数

失败返回-1

int fd_file1;

int creat("/home/abl/demo",S_IRWXU);//fd_file1为creat返回的文件描述符

2.打开(open)

函数原型:

int open( const char *pathname,int flags);//pathname:指向文件路径的指针;flags:文件权限

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

int creat( const char *pathname,mode_t mode);

pathname:指向文件路径的指针

mode:创建模式(可读可写可执行)

flags:文件权限

2.1 mod参数选择

第一类:(只能3选1)

  • O_RDONLY 只读打开
  • O_WRONLY 只写打开
  • O_RDWR 可读可写打开

第二类:(可以多选)

1、O_CREAT若文件不存在则创建它,使用此选项时,需要同时说明第三个参数mode,用其说明该新文件的存取许可权限。

fd_file1=open("./file1",O_RDWR|O_CREAT,0600);

2、O_EXCL如果同时指定了O_CREAT,而文件已经存在,则出错(返回-1)。

fd_file1=open("./file1",O_RDWR|O_CREAT|O_EXCL,0600);
//fd_file1为open返回的文件描述符,此时若已经存在file1,open返回-1

3、O_APPEND每次写时都加到文件的尾端。

fd_file1=open("./file1",O_RDWR|O_APPEND);

4、O_TRUNC属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或只写成功打开,则将其长度截短为0。

fd_file1=open("./file1",O_RDWR|O_TRUNC);//此时可将原文件里的信息先删除后写入

5. O_NOFOLLOW  如果 pathname 参数指向的是一个符号链接, 将不对其进行解引用,直接返回错误。 

2.2 flags:文件权限
  • 可执行:0100
  • 可写:0200
  • 可读:0400
  • 可读、写:0600
  • 可读、可写、可执行:0700
  • 一定是在flags中使用了O_CREAT标志,mode记录待创建的文件的访问权限

3.open 函数使用示例

(1)使用 open 函数打开一个已经存在的文件(例如当前目录下的 app.c 文件),使用只读方式打开:

int fd = open("./app.c", O_RDONLY) if (-1 == fd) return fd;

(2)使用 open 函数打开一个已经存在的文件(例如当前目录下的 app.c 文件),使用可读可写方式打开:

int fd = open("./app.c", O_RDWR) if (-1 == fd) return fd;

(3)使用 open 函数打开一个指定的文件(譬如/home/dengtao/hello),使用可读可写方式,如果该文件是 一个符号链接文件,则不对其进行解引用,直接返回错误:

int fd = open("/home/dengtao/hello", O_RDWR | O_NOFOLLOW); if (-1 == fd) return fd;

(4)使用 open 函数打开一个指定的文件(譬如/home/dengtao/hello),如果该文件不存在则创建该文件, 创建该文件时,将文件权限设置如下:文件所属者拥有读、写、执行权限;同组用户与其他用户只有读权限。使用可读可写方式打开:

int fd = open("/home/dengtao/hello", O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); 
if (-1 == fd)
return fd;

4. 关闭(close)

可调用 close 函数关闭一个已经打开的文件,其函数原型如下所示(可通过"man 2 close"查看):

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

首先使用 close 函数需要先包含 unistd.h 头文件,当我们对文件进行 IO 操作完成之后,后续不再对文件 进行操作时,需要将文件关闭。

函数参数和返回值含义如下: fd:文件描述符,需要关闭的文件所对应的文件描述符。

返回值:如果成功返回 0,如果失败则返回-1。

三 写文件 write()、读文件 read()

3.1 write 写文件

调用 write 函数可向打开的文件写入数据,其函数原型如下所示(可通过"man 2 write"查看):

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

首先使用 write 函数需要先包含 unistd.h 头文件。

  • buf:指定写入数据对应的缓冲区。
  • count:指定写入的字节数。
  • 返回值:如果成功将返回写入的字节数(0 表示未写入任何字节),如果此数字小于 count 参数,这不 是错误,譬如磁盘空间已满,可能会发生这种情况;如果写入出错,则返回-1。

对于普通文件(我们一般操作的大部分文件都是普通文件,譬如常见的文本文件、二进制文件等),不 管是读操作还是写操作,一个很重要的问题是:从文件的哪个位置开始进行读写操作?也就是 IO 操作所对 应的位置偏移量,读写操作都是从文件的当前位置偏移量处开始,当然当前位置偏移量可以通过 lseek 系统 调用进行设置,关于此函数后面再讲;默认情况下当前位置偏移量一般是 0,也就是指向了文件起始位置, 当调用 read、write 函数读写操作完成之后,当前位置偏移量也会向后移动对应字节数,譬如当前位置偏移 量为 1000 个字节处,调用 write()写入或 read()读取 500 个字节之后,当前位置偏移量将会移动到 1500 个字 节处。

3.2 read 读文件

调用 read 函数可从打开的文件中读取数据,其函数原型如下所示(可通过"man 2 read"查看): #include ssize_t read(

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

首先使用 read 函数需要先包含 unistd.h 头文件。

函数参数和返回值含义如下:

  • fd:文件描述符。与 write 函数的 fd 参数意义相同。
  • buf:指定用于存储读取数据的缓冲区。
  • count:指定需要读取的字节数。
  • 返回值:如果读取成功将返回读取到的字节数,实际读取到的字节数可能会小于 count 参数指定的字节 数,也有可能会为 0,譬如进行读操作时,当前文件位置偏移量已经到了文件末尾。实际读取到的字节数少 于要求读取的字节数,譬如在到达文件末尾之前有 30 个字节数据,而要求读取 100 个字节,则 read 读取成 功只能返回 30;而下一次再调用 read 读,它将返回 0(文件末尾)。

四 文件读写位置偏移量。

4.1 lseek

对于每个打开的文件,系统都会记录它的读写位置偏移量,我们也把这个读写位置偏移量称为读写偏移 量,记录了文件当前的读写位置,当调用 read()或 write()函数对文件进行读写操作时,就会从当前读写位置 偏移量开始进行数据读写。

读写偏移量用于指示 read()或 write()函数操作时文件的起始位置,会以相对于文件头部的位置偏移量来 表示,文件第一个字节数据的位置偏移量为 0。

当打开文件时,会将读写偏移量设置为指向文件开始位置处,以后每次调用 read()、write()将自动对其 进行调整,以指向已读或已写数据后的下一字节,因此,连续的调用 read()和 write()函数将使得读写按顺序 递增,对文件进行操作。我们先来看看 lseek 函数的原型,如下所示(可通过"man 2 lseek"查看):

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

函数参数和返回值含义如下:

  • fd:文件描述符。
  • offset:偏移量,以字节为单位。
  • whence:用于定义参数 offset 偏移量对应的参考值,该参数为下列其中一种(宏定义):
  1.  SEEK_SET:读写偏移量将指向 offset 字节位置处(从文件头部开始算);
  2. SEEK_CUR:读写偏移量将指向当前位置偏移量 + offset 字节位置处,offset 可以为正、也可以为 负,如果是正数表示往后偏移,如果是负数则表示往前偏移;
  3. SEEK_END:读写偏移量将指向文件末尾 + offset 字节位置处,同样 offset 可以为正、也可以为负, 如果是正数表示往后偏移、如果是负数则表示往前偏移。

返回值:成功将返回从文件头部开始算起的位置偏移量(字节为单位),也就是当前的读写位置;发生 错误将返回-1。

4.2使用示例:

(1)将读写位置移动到文件开头处: 

ff_t off = lseek(fd, 0, SEEK_SET); if (-1 == off) return -1;

(2)将读写位置移动到文件末尾:

off_t off = lseek(fd, 0, SEEK_END); if (-1 == off) return -1;

(3)将读写位置移动到偏移文件开头 100 个字节处:

off_t off = lseek(fd, 100, SEEK_SET); if (-1 == off) return -1;

(4)获取当前读写位置偏移量:

off_t off = lseek(fd, 0, SEEK_CUR); if (-1 == off) return -1;

函数执行成功将返回文件当前读写位置。

五:案例练习

题目1

(1)打开一个已经存在的文件(例如 src_file),使用只读方式;然后打开一个新建文件(例如 dest_file), 使用只写方式,新建文件的权限设置如下: 文件所属者拥有读、写、执行权限; 同组用户与其他用户只有读权限。 从 src_file 文件偏移头部 500 个字节位置开始读取 1Kbyte 字节数据,然后将读取出来的数据写入到 dest_file 文件中,从文件开头处开始写入,1Kbyte 字节大小,操作完成之后使用 close 显式关闭所有文件, 然后退出程序。

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

void main()
{
    char buff[ 1024];
    int fd  = open("./file",O_RDONLY);
    if(-1 == fd)
        printf("error\n");
    int fd1 = open("./test_file",O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP|S_IROTH);
    //文件所属者拥有读、写、执行权限; 同组用户与其他用户只有读权限,
    //O_TRUNC,文件中本来是有内容的,先清除再写入
    if(-1 == fd1)
        printf("error\n");

    lseek(fd, 500, SEEK_SET);
    int len = read(fd,buff,1024);
    int len2 = write(fd1,buff,len);
    printf("在file读取了 %d 字节 \n将%d字节写入test_file\n",len,len2);
    close(fd);
    close(fd1);

} 

题目2

(2)通过 open 函数判断文件是否存在(例如 test_file),并将判断结果显示出来。

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

void main(int argc,char **argv)
{
    int fd = open(argv[1],O_RDONLY);
    printf("fd = %d\n",fd);
    if(-1 == fd)  //文件打开失败
    {
        printf("error , no:%s\n",argv[1]);//没找到需要的文件
    }
    else//文件打开成功
    {
        printf("find %s susser\n",argv[1]);//打印出存在的文件名
    }
    close(fd);
}


笔者在 /home/chen/桌面路径下的文件夹中有test_file,而没有test_fil,所以执行./a.out /home/chen/桌面/test_file 这条指令时,fd = 3,所以找到了需要的文件

题目3

(3)新建一个文件(例如 new_file),新建文件的权限设置为: 文件所属者拥有读、写、执行权限;同组用户与其他用户只有读权限。 使用只写方式打开文件,将文件前 1Kbyte 字节数据填充为 0x00,将下 1Kbyte 字节数据填充为 0xFF, 操作完成之后显式关闭文件,退出程序。

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

void main()
{
    char buf[] = {"0x00"};
    char *buf1 = "0xFF";
    int fd = open("./new_file",O_WRONLY|O_CREAT,S_IRWXU| S_IRGRP| S_IRGRP);
    if(-1 == fd)
    puts("open new_file error\n");

    int LEN = 0; //统计以写入字节数
    /*写入1000字节 0x00部分*/
    while(LEN<1000)
    {
    int len = write(fd,buf,4); //len为每次写入成功的字节数
    LEN = LEN+len;
    if( 0 == LEN%100){  //每写入100字节的0x00换一次行,方便看数据,换行符不计入题目的字符字节数
         write(fd,"\n",strlen("\n"));
        }
    }
    /*写入1000字节 0xFF部分*/
    LEN = 0;
    while(LEN<1000)
    {
    int len = write(fd,buf1,4);
    LEN = LEN+len;
    if( 0 == LEN%100){  //每写入100字节的0x00换一次行,方便看数据,换行符不计入题目的字符字节数
         write(fd,"\n",strlen("\n"));
    }
    }
    close(fd);
}

 执行结果:

题目4

(4)打开一个已经存在的文件(例如 test_file),通过 lseek 函数计算该文件的大小,并打印出来。

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

void main(int argc,char **argv)
{
    int fd = open(argv[1],O_RDONLY);
    int len = lseek(fd,0,SEEK_END);
    printf("文件%s 的大小为%d个字节\n",argv[1],len);
    close(fd);
}

 指令格式为./a.out 《需要计算文件大小的文件名加路径》

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@ChenPi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值