Linux系统调用函数open()、标准C库函数fopen()函数讲解以及它们之间的使用区别

前言

如果,想要深入的学习Linux系统调用函数lseek(),以及标准C库函数fopen()的话,还是需要自己去阅读Linux系统中的帮助文档的。
具体输入命令:

man 2 open
man 3 fopen

即可查阅到完整的资料信息。

open 函数

open() 是一个系统调用函数,用于在 Linux 系统中打开或者创建一个文件。

函数原型:

// 使用这个函数需导入以下头文件
#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);

参数:

  • pathname:这是要打开或创建的文件的路径名。
  • flags:这是一个位掩码,用于决定文件的访问模式和行为。常用的标志有以下几种:
    • O_RDONLY:只读打开。
    • O_WRONLY:只写打开。
    • O_RDWR:读写打开。
    • O_CREAT:如果文件不存在,就创建新文件。
    • O_TRUNC:如果文件已存在,并且是以写入(O_WRONLY 或 O_RDWR)方式打开,则将其长度截断为0。
    • O_APPEND:在每次写入时,都从文件末尾开始添加。
  • mode:当使用 O_CREAT 标志时,该参数用于指定新文件的权限。它应该是一个八进制数,通常使用文件权限宏进行设置(例如,S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)。

返回值:

  • open()函数返回一个文件描述符,这是一个小的非负整数,用于在后续的系统调用(例如 read(), write(), lseek(), 或 close())中引用打开的文件。

  • 如果 open()调用成功,返回的文件描述符一定是最小的未使用的文件描述符数字。

  • 如果函数调用失败,会返回-1,并设置 errno 来表示错误类型。

关于 open() 的更多细节和特性

  • 文件描述符(File Descriptors): 当你使用 open() 打开一个文件时,系统会返回一个文件描述符,这是一个整数,被用作后续操作(如读、写或关闭文件)的标识符。文件描述符是进程资源的一部分,每个进程都有自己的文件描述符表。这意味着不同的进程可以打开相同的文件而得到不同的文件描述符,而这些文件描述符在各自的进程中是唯一的。

  • 并发性(Concurrency): 如果多个进程同时打开同一个文件,可能会产生一些并发问题。为了解决这些问题,open() 提供了一些额外的标志,如 O_EXCL(与 O_CREAT 一起使用时,如果文件已存在,open() 将失败)和 O_SYNC(写操作会等待物理 I/O 完成)。

  • 文件偏移(File Offsets): 当你打开一个文件时,文件偏移(文件中的当前读/写位置)通常会被设置为文件的开头。如果你使用 O_APPEND 标志打开文件,每次写操作都会将文件偏移移到文件的末尾,这样新数据就会被追加到文件的末尾。

  • 错误处理(Error Handling): 如果 open() 函数调用失败,它会返回 -1 并设置全局变量 errno 来指示错误。你可以使用 <errno.h> 中定义的错误宏来判断错误类型,也可以使用 perror() 或 strerror() 函数来获取错误描述。

下面是使用 open() 函数的一个例子,它打开一个文件进行写操作,如果文件不存在则创建它:

#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

int main() {
    int fd;

    fd = open("testfile.txt", O_WRONLY | O_CREAT, S_IRWXU);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    /* 这里可以进行读写操作 */

    close(fd);  // 记得关闭文件

    return 0;
}

在这个例子中,如果 open() 调用失败,我们使用 perror() 函数打印出错误信息。S_IRWXU 是文件权限,表示文件所有者有读、写和执行权限。

fopen函数

fopen() 函数是标准 C 库中用来打开文件的函数。

它的原型如下:

#include <stdio.h> // 使用此函数需导入此头文件

FILE *fopen(const char *filename, const char *mode);

参数说明如下:

  • filename:这是要打开或创建的文件的路径名。
  • mode:这是一个字符串,表示文件的访问模式。常用的模式包括:
    • “r”:只读打开。文件必须存在。
    • “w”:只写打开。如果文件存在,会被截断为零长度。如果文件不存在,则尝试创建文件。
    • “a”:追加写模式。如果文件存在,数据会被追加到文件末尾。如果文件不存在,尝试创建文件。
    • “r+”:读写打开。文件必须存在。
    • “w+”:读写打开。如果文件存在,会被截断为零长度。如果文件不存在,则尝试创建文件。
    • “a+”:读写打开。如果文件存在,数据会被追加到文件末尾。如果文件不存在,则尝试创建文件。

返回值:

  • 如果 fopen() 函数调用成功,则返回一个 FILE * 指针,这是一个文件流对象,用于在后续的 I/O 函数(例如 fread(), fwrite(), fseek(), 或 fclose())中引用打开的文件。

  • 如果 fopen() 调用失败,会返回 NULL,并设置 errno 来表示错误类型。

关于 fopen() 函数的一些其他的细节和特性

  • 二进制模式和文本模式: 在某些平台(如 Windows)上,fopen() 可以在文本模式(默认)和二进制模式之间切换。在文本模式下,某些字符(如换行和回车)可能会被自动转换。在二进制模式下,数据将原样读写,不进行任何转换。你可以通过在模式字符串中加入 b 或 t 来指定二进制模式或文本模式。例如,“rb” 表示以二进制模式读取,“wt” 表示以文本模式写入。

  • 文件流(File Streams): fopen() 返回的 FILE * 指针实际上是一个文件流。这是 C 标准库对文件 I/O 进行抽象的方式,它将文件视为字节序列,并提供一种统一的方式来处理各种类型的 I/O 设备。文件流还提供了缓冲,可以减少物理 I/O 操作的次数,从而提高性能。

  • 错误处理: 如果 fopen() 调用失败,你可以通过查看 errno 来确定错误类型。你也可以使用 perror() 或 strerror() 函数来获取错误描述。

  • 关闭文件: 使用 fopen() 打开的文件必须使用 fclose() 来关闭。如果你忘记关闭文件,可能会导致资源泄漏,这是一种常见的编程错误。即使在写入文件时出现错误,你也应该关闭文件,因为 fclose() 会尝试刷新任何未写入的数据。

这是一个使用 fopen() 的例子,它打开一个文件进行写操作,并在文件中写入一些数据:

#include <stdio.h>

int main() {
    FILE *fp;

    fp = fopen("testfile.txt", "w");
    if (fp == NULL) {
        perror("fopen");
        return 1;
    }

    if (fputs("Hello, World!\n", fp) == EOF) {
        perror("fputs");
        return 1;
    }

    if (fclose(fp) == EOF) {
        perror("fclose");
        return 1;
    }

    return 0;
}

在这个例子中,我们使用 fopen() 打开一个文件,使用 fputs() 写入数据,然后使用 fclose() 关闭文件。如果任何函数调用失败,我们都使用 perror() 打印出错误信息。

open函数 与 fopen 函数的区别

open() 和 fopen() 都可以用来在程序中打开文件,但它们之间有一些主要的区别:

  • 库和系统调用

    • open() 是一个系统调用,直接由操作系统内核提供。它提供了非常低级的文件访问,并且在所有 Unix 和类 Unix 系统(包括 Linux)上可用。
    • 相比之下,fopen() 是 C 标准库函数,提供了更高级别的接口,包括缓冲和文件位置管理。由于 fopen() 是库函数,所以它在所有支持 C 标准库的系统上都可用,包括非 Unix 系统。
  • 返回值:

    • open() 返回一个文件描述符,这是一个整数,用于在后续的系统调用(例如 read(), write(), lseek(), 或 close())中引用打开的文件。
    • 相比之下,fopen() 返回一个 FILE * 指针,这是一个指向 FILE 结构的指针,用于在后续的库调用(例如 fread(), fwrite(), fseek(), 或 fclose())中引用打开的文件。
  • 错误处理

    • open() 和 fopen() 都会在出错时返回特殊的值(对于 open() 是 -1,对于 fopen() 是 NULL),并设置全局变量 errno 来表示错误类型。
    • 然而,C 标准库还提供了一些额外的错误处理函数,例如 perror() 和 strerror()。
  • 缓冲:

    • fopen() 提供的文件 I/O 是缓冲的,这意味着数据通常会在内存中积累,直到达到一定的量后才会真正写入磁盘。这可以提高效率,因为磁盘 I/O 通常比内存操作慢得多。
    • 而 open() 提供的是无缓冲 I/O,数据会直接读写到磁盘。
  • 访问模式:

    • open() 和 fopen() 都允许你指定文件的访问模式,如只读、只写或读写。
    • 然而,fopen() 还允许你在文本模式和二进制模式之间选择,这在处理非文本文件(如图像或音频文件)时很有用。

在大多数情况下,fopen() 提供的高级接口和缓冲 I/O 是更好的选择。然而,如果你需要更低级别的控制,或者需要使用某些特殊的文件操作(如 fcntl() 或 ioctl() 系统调用),那么 open() 可能会更适合。

总结

  • open() 是 Linux 系统调用,用于打开或创建文件。它返回一个文件描述符,用于后续的文件操作。open() 提供了底层的、无缓冲的 I/O,直接与操作系统内核交互。

  • fopen() 是标准 C 库函数,也用于打开或创建文件。它返回一个 FILE * 指针,用于后续的文件操作。fopen() 提供了更高级别的接口,包括缓冲和文件位置管理。由于 fopen() 是库函数,所以它在所有支持 C 标准库的系统上都可用。

  • 两者都可以用来在程序中打开文件,但是 fopen() 提供了更高级别的接口,包括错误处理和文件位置管理。open() 提供了更直接的、底层的文件访问。

  • 错误处理方面,两者都会在出错时返回特殊的值,并设置全局变量 errno 来表示错误类型。然而,C 标准库还提供了一些额外的错误处理函数,例如 perror() 和 strerror()。

总的来说,如果你在编写一个标准的、跨平台的C程序,fopen() 由于其缓冲 I/O 和跨平台的特性,可能是你的最佳选择。然而,如果你在编写一个需要更精细控制文件系统的Unix或Linux程序,需要更低级别的控制,或者需要使用某些特殊的文件操作,那么 open() 可能会更适合。

最后的最后,如果你觉得我的这篇文章写的不错的话,请给我一个赞与收藏,关注我,我会继续给大家带来更多更优质的干货内容

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿宋同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值