前言
如果,想要深入的学习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() 可能会更适合。
最后的最后,如果你觉得我的这篇文章写的不错的话,请给我一个赞与收藏,关注我,我会继续给大家带来更多更优质的干货内容
。