概述
在Linux系统中,文件操作是一项基本而又重要的任务,主要依赖于系统调用。系统调用是操作系统提供的底层接口,允许用户程序直接与内核进行通信。常用的文件操作包括:打开文件、关闭文件、读取文件、写入文件、文件定位等。下面,我们分别进行介绍。
打开文件
open函数用于打开或创建文件,并设置相应的访问模式和权限。其函数原型如下。
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname:文件路径,可以是绝对路径或相对路径。
flags:打开文件的模式,常见的标志位如下。
(1)O_RDONLY:只读模式。
(2)O_WRONLY:只写模式。
(3)O_RDWR:读写模式。
(4)O_CREAT:如果文件不存在,则创建文件。
(5)O_EXCL:与O_CREAT一起使用,如果文件已存在,则调用失败。
(6)O_TRUNC:如果文件已存在且为常规文件,则将其长度截断为0。
(7)O_APPEND:每次写入时,文件指针都会移动到文件末尾。
mode:文件权限,仅在创建新文件时使用。权限可以用八进制表示,常见的权限值包括:
(1)0644:所有者可读写,其他用户只读。
(2)0755:所有者可读写执行,其他用户可读执行。
返回值:成功时,返回一个非负整数,称为文件描述符,用于后续的文件操作。失败时,返回-1,并设置errno为适当的错误码。常见的错误码如下。
(1)EACCES:权限不足。
(2)EEXIST:文件已存在(当使用O_CREAT和O_EXCL 标志时)。
(3)ENOENT:文件或路径不存在。
(4)ENOMEM:内存不足。
(5)ENFILE:系统文件表已满。
(6)EMFILE:进程已达到文件描述符限制。
关闭文件
close函数用于关闭之前打开的文件,并释放系统资源。其函数原型如下。
int close(int fd);
fd:要关闭的文件描述符,通常为调用open或其他文件打开函数时返回的非负整数。
返回值:成功时,返回0。失败时,返回-1。
注意:打开的文件应及时关闭,以释放系统资源。忘记关闭文件可能会导致文件描述符耗尽,影响程序的性能和稳定性。
读取文件
read函数用于从文件描述符中读取数据,其函数原型如下。
ssize_t read(int fd, void *buf, size_t count);
fd:要读取的文件描述符。
buf:存放读取数据的缓冲区。
count:要读取的最大字节数。
返回值:成功时,返回实际读取的字节数。如果读取到文件末尾,返回0。失败时,返回-1,并设置errno为适当的错误码。常见的错误码如下。
(1)EBADF:文件描述符无效。
(2)EINTR:系统调用被信号中断。
(3)EIO:发生I/O错误。
(4)EFAULT:缓冲区地址无效。
写入文件
write函数用于将数据写入文件描述符,其函数原型如下。
ssize_t write(int fd, const void *buf, size_t count);
fd:要写入的文件描述符。
buf:指向要写入的数据缓冲区的指针。
count:要写入的字节数。
返回值:成功时,返回实际写入的字节数。失败时,返回-1,并设置errno为适当的错误码。常见的错误码如下。
(1)EBADF:文件描述符无效。
(2)EINTR:系统调用被信号中断。
(3)EIO:发生I/O错误。
(4)EFAULT:缓冲区地址无效。
(5)ENOSPC:磁盘空间不足。
文件定位
lseek函数用于移动文件指针到指定位置,移动后,可从该位置继续读写文件。其函数原型如下。
off_t lseek(int fd, off_t offset, int whence);
fd:要操作的文件描述符。
offset:相对于whence的偏移量。
whence:移动方式,常见的取值如下。
(1)SEEK_SET:从文件开头开始计算偏移量。
(2)SEEK_CUR:从当前文件指针位置开始计算偏移量。
(3)SEEK_END:从文件末尾开始计算偏移量。
返回值:成功时,返回新的文件指针位置,以字节为单位。失败时,返回-1,并设置errno为适当的错误码。常见的错误码如下。
(1)EBADF:文件描述符无效。
(2)EINVAL:无效的whence值。
(3)ESPIPE:文件描述符不支持定位操作。
实战代码
下面的实战代码展示了如何进行文件的基本操作,包括上面介绍的打开文件、关闭文件、读取文件、写入文件和文件定位。
首先,我们使用open函数以读写模式打开或创建文件hope_wisdom.txt。如果文件不存在,则创建,并设置文件权限为0644。如果打开文件失败,则使用perror输出错误信息并退出程序。
接下来,使用write函数将字符串写入文件,写入的字节数为字符串的长度。如果写入失败,使用perror输出错误信息并关闭文件后退出程序。成功写入后,输出写入的字节数。
然后,使用lseek函数将文件指针移动到文件开头。成功移动后,输出文件指针已移动到文件开头的信息。
紧接着,定义一个缓冲区buffer,并清空缓冲区。使用read函数从文件中读取数据到缓冲区,最多读取缓冲区大小 - 1个字节。成功读取后,输出读取的字节数和读取的内容。
最后,我们使用close函数关闭文件描述符。成功关闭文件后,输出文件已关闭的信息。
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
using namespace std;
int main()
{
// 打开文件,读写模式,如果文件不存在则创建
int fd = open("hope_wisdom.txt", O_RDWR | O_CREAT, 0644);
if (fd == -1)
{
perror("open");
return EXIT_FAILURE;
}
cout << "File opened: " << fd << endl;
// 写入文件内容
const char *pszContent = "Hello, World!\n";
ssize_t bytes_written = write(fd, pszContent, strlen(pszContent));
if (bytes_written == -1)
{
perror("write");
close(fd);
return EXIT_FAILURE;
}
cout << "Write " << bytes_written << " bytes to file" << endl;
// 移动文件指针到文件开头
off_t offset = lseek(fd, 0, SEEK_SET);
if (offset == -1)
{
perror("lseek");
close(fd);
return EXIT_FAILURE;
}
cout << "File pointer moved to the beginning" << endl;
// 读取文件内容
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read == -1)
{
perror("read");
close(fd);
return EXIT_FAILURE;
}
cout << "Read " << bytes_read << " bytes from file: " << buffer << endl;
// 关闭文件
if (close(fd) == -1)
{
perror("close");
return EXIT_FAILURE;
}
cout << "File closed" << endl;
return 0;
}