当前位置:首页 » 《关注互联网》 » 正文

【Linux】文件操作函数 (详解)

1 人参与  2024年11月03日 08:42  分类 : 《关注互联网》  评论

点击全文阅读


 ?个人主页:island1314

?个人专栏:Linux—登神长阶

⛺️ 欢迎关注:?点赞 ??留言 ?收藏  ? ? ?


前言 ?

? 每个系统都有自己的专属函数,我们习惯称其为系统函数。系统函数并不是内核函数,因为内核函数是不允许用户使用的,系统函数就充当了二者之间的桥梁,这样用户就可以间接的完成某些内核操作了。
如:open、close、lseek、read、write这些系统IO函数又被称为不带缓冲的IO (unbuffered IO)。术语不带缓冲指的是每个read和write都调用内核中的一个系统调用,因此也常叫做系统IO,与之相对应的还有标准IO(fopen、fread、fwrite、fclose等)。
应用层程序编写如下:

直接调用系统层接口IO(即open、 read、 write 等函数)另一种则是调用后面C库的接口IO(即fopen、 fread、 fwrite 等 函数)间接地调用系统调用层接口。

1. 系统 IO

 1.1 open 函数 - 流打开

其实这个函数 我们之前在这篇博客里【Linux】基础 IO(文件描述符fd & 缓冲区 & 重定向)​​​​​​

 提过的,大家可以去看看,以及下面的 close 和 write 也有说明

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h> /* * @description   : 调用open可以打开或创建一个文件 * @param - path  : 指定打开或创建的文件的文件名 * @param - flags : 指定文件的打开方式,选项后面有说明 * @param - mode  : 在创建新文件的时候才需要指定这个参数的值,用于指定新文件的权限(常用值0664==rw-rw-r--); * @return   : 若成功,返回内核分配的文件描述符(大于0),失败返回-1; */int open(const char *path, int flags, mode_t mode);

flags参数指定了文件打开方式等信息。用下列一个或多个宏常量进行“或 | ”运算构成flags参数:
(这些常量在头文件<fcntl.h>中定义)。
以下为必选属性 ,在这五个宏常量属性中必须指定一个且只能指定一个

flags说明
O_RDONLY以只读方式打开文件
O_WRONLY以只写方式打开文件
O_RDWR.以读写方式打开文件
O_EXEC只执行打开
O_SEARCH只搜索打开(应用于目录)

以下为可选属性 , 可以和上边的属性一起使用。

flags说明
O_APPEND新数据追加到文件尾部,不会覆盖文件的原来内容
O_CREAT若此文件不存在则创建它,后面必须跟mode参数指定该新文件的访问权限。如果文件存在什么也不做

1.2 write 函数 - 流写入

#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);//返回值:若成功,返回已写的字节数(带符号整型);若出错,返回-1fd:指定需要偏移操作的文件描述符buf:字符串count:长度

返回值:通常与参数count的值相同,否则表示出错。
write 出错的一个常见原因:① 磁盘已写满,② 超过了一个给定进程的文件长度限制。

注意:对于普通文件,写操作从文件的当前偏移量处开始如果在打开该文件时,指定了O_APPEND选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处。在一次成功写之后,该文件偏移量增加实际写的字节数。

1.3 read 函数 - 流读取

#include <unistd.h>ssize_t read(int fd, void *buf, size_t count); //返回值:若成功,返回读的字节数(带符号整型);若已到文件尾,返回0;出错,返回-1fd:指定需要偏移操作的文件描述符buf:字符串count:长度

有多种情况可使实际读到的字节数少于要求读的字节数:

读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前有30个字节,而要求读100个字节,则read返回30。下一次再调用read时,它将返回 0 (文件尾端)当从终端设备读时,通常一次最多读一行当从网络读时,网络中的缓冲机制可能造成返回值小于所要求读的字节数。当从管道或FIFO读时,如若管道包含的字节少于所需的数量,那么read将只返回实际可用的字节数。 当从某些面向记录的设备(如磁带〉读时,一次最多返回一个记录。当一信号造成中断,而已经读了部分数据量时。读操从文件的当前偏移量处开始。在成功返问之前,该偏移量将增加实际读到的字节数。

1.4 close 函数 - 流关闭

#include <unistd.h>/* * @description  : 调用close可以关闭一个已打开的文件 * @param - fd : 指定关闭的文件的描述符; * @return  : 若成功,返回0,失败返回-1; */int close(int fd);

关闭一个文件时还会释放该进程加在该文件上的所有记录锁。
当一个进程终止时,如果不调用close()手动关闭打开的文件,内核将自动关闭它所有的打开文件

1.5 lseek 函数 - 定位流

每个打开文件都有一个与其相关联的 “当前文件偏移量”
它通常是一个非负整数,用以度量从文件开始处计算的字节数。
通常,读、写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。

按系统默认的情况,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0

当然也可以调用 lseek 显式地为一个打开文件设置偏移量。

#include <sys/types.h>#include <unistd.h>/* * @description    : 调用lseek函数可以移动文件指针,也可以通过这个函数进行文件的拓展。 * @param - fd   : 指定需要偏移操作的文件描述符; * @param - offset : 指定偏移量,需要和第三个参数配合使用 * @param - whence : 通过这个参数指定函数实现什么样的功能: * SEEK_SET: 从文件头部开始偏移 offset 个字节 * SEEK_CUR: 从当前文件指针的位置向后偏移 offset 个字节 * SEEK_END: 从文件尾部向后偏移 offset 个字节 * @return    : 若成功,返回文件指针从头部开始计算总的偏移量;出错,返回-1 */off_t lseek(int fd, off_t offset, int whence);

对参数offset的解释与参数whence的值(符号常量)有关:

若whence是 SEEK_SET,则将该文件的偏移量设置为距文件开始处 offset个字节若whence是 SEEK_CUR,则将该文件的偏移量设置为其当前值加 offset,offset可为正或负若whence是 SEEK_END,则将该文件的偏移量设置为文件长度加 offset,offset可为正或负

1.6 综合样例

使用代码打开当前路径下的“bite”文件(如果文件不存在在创建文件),向文件当中写入“i like linux!”.

#include <stdio.h>#include <unistd.h>//是close, write这些接口的头文件#include <string.h>#include <fcntl.h>//是 O_CREAT 这些宏的头文件#include <sys/stat.h>//umask接口头文件int main(){    //将当前进程的默认文件创建权限掩码设置为0--- 并不影响系统的掩码,仅在当前进程内生效    umask(0);    //int open(const char *pathname, int flags, mode_t mode);    int fd = open("./bite", O_CREAT|O_RDWR, 0664);    if(fd < 0) {        perror("open error");        return -1;     }       char *data = "i like linux\n!";    //ssize_t write(int fd, const void *buf, size_t count);    ssize_t ret = write(fd, data, strlen(data));    if (ret < 0) {        perror("write error");        return -1;     }       //off_t lseek(int fd, off_t offset, int whence);    lseek(fd, 0, SEEK_SET);    char buf[1024] = {0};    //ssize_t read(int fd, void *buf, size_t count);    ret = read(fd, buf, 1023);    if (ret < 0) {        perror("read error");        return -1;     }else if (ret == 0) {        printf("end of file!\n");        return -1;     }       printf("%s", buf);    close(fd);    return 0;}

运行结果如下:

2. 标准 IO

2.1 fopen 函数 - 流打开

#incldue<stdio.h> FILE * fopen(const char *pathname, const char *method);

功能:用于打开文件 

参数:

pathname:被打开文件的文件路径以及文件名。method:打开文件的方式。

具体方式如下:

“r" 或 ”rb" 以只读方式打开文件。“w" 或 ”wb" 以写方式打开文件,新内容会覆盖原本内容。“a” 或 “ab” 以写方式打开文件,新内容追加在文件末尾。
 FILE *fp;//定义一个指向FILE结构的指针   // 在当前路径用可读可写打开一个“ccc.txt”的文件,如果不存在则创建它    fp=fopen("./ccc.txt","w+");

返回值

成功:它返回一个指向FILE结构的指针,该结构代表这个新创建的流(文件顺利打开后,指向该流的文件指针就会被返回)

失败:它就会返回一个空指针,errno会提示问题的性质(如果文件打开失败,则返回NULL,并把错误代码存在errno中)

当打开一个流时,标准IO函数fopen返回一个指向FILE对象的指针( 文件指针)。
该对象通常是一个结构,它包含了标准IO库为管理该流需要的所有信息(流),
包括用于实际IO 的文件描述符、指向用于该流缓冲区的指针、缓冲区的长度、当前在缓冲区中的字符数以及出错标志等。

为了引用一个流,需将 FILE* 指针作为参数传递给每个标准IO函数

2.2 fwrite 函数 - 流写入

size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数:

ptr – 是一个指针,对fwrite来说,是要输出数据的地址size – 这是要读取的每个元素的大小,以字节为单位nmemb – 这是元素的个数,每个元素的大小为 size 字节stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流

返回值

如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误。

2.3 fread 函数 - 流读取

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数:

ptr – 是一个指针,对fread来说,它是读入数据的存放地址。size – 这是要读取的每个元素的大小,以字节为单位。nmemb – 这是元素的个数,每个元素的大小为 size 字节。stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。

返回值

成功读取的元素总数会以 size_t nmemb对象返回,size_t 对象是一个整型数据类型。如果总数与 nmemb 参数不同,则可能发生了一个错误或者到达了文件末尾

2.4 fclose 函数 - 流关闭

int fclose( FILE *file )

返回值:

对于输出流,fclose函数会在文件关闭前刷新缓冲区,如果它执行成功,fclose返回0

在该文件被关闭之前,冲洗缓冲中的输出数据。

缓冲区中的任何输入数据被丢弃。如果标准I/O库已经为该流自动分配了一个缓冲区,则释放此缓冲区。当一个进程正常终止时(直接调用exit函数,或从main函数返回),

则所有带未写缓冲数据的标准IO流都被冲洗,所有打开的标准IO流都被关闭

 

2.5 fseek 函数 - 定位流

int fseek(FILE *stream, long offset, int whence);

参数:

stream: 文件指针。offset: 偏移量,就是相当于当前位置,向左(右)移动几位。正数表示右向偏移,负数表示左向偏移。whence: 定义文件中哪里开始偏移,取值可为:SEEK_CUR(当前位置)、 SEEK_END (文件结尾)或 SEEK_SET(文件开头)。 其中SEEK_SET,SEEK_CUR和SEEK_END依次为0,1和2。

返回值:

如果执行成功,函数返回0。如果执行失败,函数返回一个非0值。

2.6 综合样例

使用代码打开当前路径下的“bite”文件(如果文件不存在在创建文件),向文件当中写入“linux so easy!”.

#include <stdio.h>#include <stdlib.h>#include <string.h>int main(){    FILE *fp = fopen("./byte", "wb+");    if(fp == NULL){         perror("fopen Error");        return -1;    }    fseek(fp, 0, SEEK_SET);//跳转读写位置到,从文件起始位置开始偏移0个字节    char *data = "linux so easy!\n";    size_t ret = fwrite(data, 1, strlen(data), fp);    if(ret != strlen(data)){        perror("fwrite Error");        return -1;    }    fseek(fp, 0, SEEK_SET);    char buf[1024] = {0};    //因为设置读取块大小位1,块个数为1023因此fread返回值为实际读取到的数据长度    ret = fread(buf, 1, 1023, fp);    if (ret == 0) {        if (ferror(fp)) //判断上一次IO操作是否正确            printf("fread error\n");        if (feof(fp)) //判断是否读取到了文件末尾            printf("read end of file!\n");        return -1;     }       printf("%s", buf);    fclose(fp);    return 0;}

运行结果如下:

3. 区别 ?

(1)缓冲机制

系统 I/O:

通常不使用缓冲,直接进行数据传输,这可能导致性能较低,因为每个 I/O 操作都涉及系统调用。

标准 I/O:

采用缓冲机制,能提高 I/O 性能,尤其是在频繁读取或写入时。标准 I/O 会将数据存储在内存中,减少对系统调用的直接需求。

(2)灵活性和可移植性

系统 I/O:

通常依赖于特定操作系统的实现,可能不具有跨平台的可移植性。

标准 I/O:

由 C 标准库定义,具有较高的可移植性,可以在不同的平台上使用相同的代码。

(3)错误处理

系统 I/O:

需要手动检查返回值和设置 errno 来处理错误。

标准 I/O:

提供了更方便的错误处理机制,可以使用 ferror() 等函数检查错误状态。

4. 总结 ?

使用场景: 如果需要底层控制和优化性能,可以使用系统 I/O;如果希望简化开发过程,使用标准 I/O 更为合适。复杂性: 标准 I/O 提供更高的抽象和易用性,适合大多数常规应用;系统 I/O 则适用于对性能和资源管理有特殊要求的场合。

【*★,°*:.☆( ̄▽ ̄)/$:*.°★* 】那么本篇到此就结束啦,如果我的这篇博客可以给你提供有益的参考和启示,可以三连支持一下 !!

 


点击全文阅读


本文链接:http://zhangshiyu.com/post/181732.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

最新文章

  • 祖母寿宴,侯府冒牌嫡女被打脸了(沈屿安秦秀婉)阅读 -
  • 《雕花锦年,昭都旧梦》(裴辞鹤昭都)完结版小说全文免费阅读_最新热门小说《雕花锦年,昭都旧梦》(裴辞鹤昭都) -
  • 郊区41号(许洛竹王云云)完整版免费阅读_最新全本小说郊区41号(许洛竹王云云) -
  • 负我情深几许(白诗茵陆司宴)完结版小说阅读_最热门小说排行榜负我情深几许白诗茵陆司宴 -
  • 九胞胎孕妇赖上我萱萱蓉蓉免费阅读全文_免费小说在线看九胞胎孕妇赖上我萱萱蓉蓉 -
  • 为保白月光,侯爷拿我抵了债(谢景安花田)小说完结版_完结版小说全文免费阅读为保白月光,侯爷拿我抵了债谢景安花田 -
  • 陆望程映川上官硕《我的阿爹是带攻略系统的替身》最新章节阅读_(我的阿爹是带攻略系统的替身)全章节免费在线阅读陆望程映川上官硕
  • 郑雅琴魏旭明免费阅读_郑雅琴魏旭明小说全文阅读笔趣阁
  • 头条热门小说《乔书意贺宴临(乔书意贺宴临)》乔书意贺宴临(全集完整小说大结局)全文阅读笔趣阁
  • 完结好看小说跨年夜,老婆初恋送儿子故意出车祸_沈月柔林瀚枫完结的小说免费阅读推荐
  • 热推《郑雅琴魏旭明》郑雅琴魏旭明~小说全文阅读~完本【已完结】笔趣阁
  • 《你的遗憾与我无关》宋怀川冯洛洛无弹窗小说免费阅读_免费小说大全《你的遗憾与我无关》宋怀川冯洛洛 -

    关于我们 | 我要投稿 | 免责申明

    Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1