在Linux系统编程中,文件IO操作是非常常见和重要的操作之一。通过文件IO操作,我们可以打开、读取、写入和关闭文件,对文件进行定位、复制、删除和重命名等操作。本篇博客将介绍一些常用的文件IO操作函数。
open()函数是Linux系统编程中常用的文件IO操作函数之一。它用于打开文件并返回一个文件描述符,以便后续的文件读写操作。
open()函数的原型如下:
#include
#include
#include
int open(const char *pathname, int flags, mode_t mode);
pathname
:要打开的文件路径名。flags
:打开文件的模式标志,可以是以下几种模式的组合:
O_RDONLY
:只读模式打开文件。O_WRONLY
:只写模式打开文件。O_RDWR
:读写模式打开文件。O_CREAT
:如果文件不存在,则创建文件。O_EXCL
:与O_CREAT一同使用,如果文件已存在,则返回错误。O_TRUNC
:如果文件存在且以可写模式打开,则将文件截断为0。O_APPEND
:在文件末尾追加数据。mode
: 创建文件时的访问权限,只有在使用O_CREAT时才有效。 下面是一个使用open()函数的示例:
#include
#include
#include
#include
#include
int main() {
int fd;
// 打开文件
fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open");
return errno;
}
// 写入数据
char buffer[] = "Hello, world!";
ssize_t ret = write(fd, buffer, sizeof(buffer) - 1);
if (ret == -1) {
perror("write");
close(fd);
return errno;
}
// 关闭文件
close(fd);
return 0;
}
在上述示例中,我们首先使用open()函数以只写模式打开一个文件example.txt。如果文件不存在,则创建该文件,并设置访问权限为用户可读可写。如果打开文件失败,我们使用perror()函数打印错误信息,并返回errno变量。
接下来,我们使用write()函数向文件中写入数据。在本例中,我们写入了字符串"Hello, world!"。如果写入数据失败,我们同样使用perror()函数打印错误信息,并在关闭文件前返回errno变量。
最后,我们使用close()函数关闭文件。
close()函数是Linux系统编程中用于关闭文件的函数。它接受一个文件描述符作为参数,并返回一个整数值来指示操作是否成功。
close()函数的原型如下:
#include
int close(int fd);
fd
:要关闭的文件描述符。errno
变量来指示错误类型。下面是一个使用close()函数的示例:
#include
#include
#include
#include
#include
#include
int main() {
int fd;
// 打开文件
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return errno;
}
// 关闭文件
int ret = close(fd);
if (ret == -1) {
perror("close");
return errno;
}
return 0;
}
在上述示例中,我们首先使用open()
函数以只读模式打开一个文件example.txt
,并将返回的文件描述符存储在变量fd
中。如果打开文件失败,我们使用perror()
函数打印错误信息,并返回errno
变量。
接下来,我们使用close()
函数关闭文件。如果关闭文件失败,我们同样使用perror()
函数打印错误信息,并返回errno
变量。
需要注意的是,关闭文件后,我们不应再对该文件描述符进行任何操作。
read()函数是Linux系统编程中用于从文件中读取数据的函数。它接受一个文件描述符、一个缓冲区地址和一个读取的最大字节数作为参数,并返回实际读取的字节数。
read()函数的原型如下:
#include
ssize_t read(int fd, void *buf, size_t count);
fd
:要读取的文件的文件描述符。buf
:用于存储读取数据的缓冲区的地址。count
:要读取的最大字节数。-1
,并设置errno
变量来指示错误类型。下面是一个使用read()函数的示例:
#include
#include
#include
#include
#include
#include
int main() {
int fd;
// 打开文件
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return errno;
}
// 读取数据
char buffer[100];
ssize_t ret = read(fd, buffer, sizeof(buffer));
if (ret == -1) {
perror("read");
close(fd);
return errno;
}
// 输出读取的数据
printf("Read %ld bytes: %s\n", ret, buffer);
// 关闭文件
close(fd);
return 0;
}
在上述示例中,我们首先使用open()
函数以只读模式打开一个文件example.txt
,并将返回的文件描述符存储在变量fd
中。如果打开文件失败,我们使用perror()
函数打印错误信息,并返回errno
变量。
接下来,我们使用read()
函数从文件中读取数据。我们定义一个长度为100的缓冲区buffer
,并将其作为参数传递给read()
函数。read()
函数将尽量读取count
个字节的数据,并将其存储在缓冲区中。如果读取数据失败,我们同样使用perror()
函数打印错误信息,并在关闭文件前返回errno
变量。
最后,我们使用printf()
函数输出读取的数据,并使用close()
函数关闭文件。
需要注意的是,read()函数是一个阻塞函数,如果文件中没有足够的数据可读,它将一直等待直到有足够的数据可读或者发生错误。如果需要非阻塞地读取数据,可以使用fcntl()函数设置文件描述符为非阻塞模式。
write()函数是Linux系统编程中用于向文件中写入数据的函数。它接受一个文件描述符、一个数据缓冲区地址和要写入的字节数作为参数,并返回实际写入的字节数。
write()函数的原型如下:
#include
ssize_t write(int fd, const void *buf, size_t count);
fd
:要写入的文件的文件描述符。buf
:要写入的数据的缓冲区的地址。count
:要写入的字节数。-1
,并设置errno
变量来指示错误类型。下面是一个使用write()函数的示例:
#include
#include
#include
#include
#include
#include
int main() {
int fd;
// 打开文件
fd = open("example.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open");
return errno;
}
// 写入数据
char buffer[] = "Hello, World!";
ssize_t ret = write(fd, buffer, sizeof(buffer) - 1);
if (ret == -1) {
perror("write");
close(fd);
return errno;
}
// 关闭文件
close(fd);
return 0;
}
在上述示例中,我们首先使用open()
函数以只写模式打开一个文件example.txt
,并将返回的文件描述符存储在变量fd
中。如果打开文件失败,我们使用perror()
函数打印错误信息,并返回errno
变量。
接下来,我们使用write()
函数将数据写入文件中。我们定义一个字符串buffer
,并将其作为参数传递给write()
函数。write()
函数将尽量写入count
个字节的数据到文件中。如果写入数据失败,我们同样使用perror()
函数打印错误信息,并在关闭文件前返回errno
变量。
最后,我们使用close()
函数关闭文件。
需要注意的是,write()函数是一个阻塞函数,如果文件无法立即接受写入的数据(例如,磁盘空间不足),它将一直等待直到可以写入数据或者发生错误。如果需要非阻塞地写入数据,可以使用fcntl()函数设置文件描述符为非阻塞模式。
lseek()函数是Linux系统编程中用于在文件中定位读写位置的函数。它接受一个文件描述符、一个偏移量和一个起始位置作为参数,并返回新的读写位置。
lseek()函数的原型如下:
#include
off_t lseek(int fd, off_t offset, int whence);
fd
:要定位的文件的文件描述符。offset
:偏移量,可以是正数、负数或零。whence
:起始位置,可以取以下三个值:
SEEK_SET
:从文件开头开始计算偏移量。SEEK_CUR
:从当前读写位置开始计算偏移量。SEEK_END
:从文件末尾开始计算偏移量。-1
,并设置errno
变量来指示错误类型。下面是一个使用lseek()函数的示例:
#include
#include
#include
#include
#include
#include
int main() {
int fd;
// 打开文件
fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return errno;
}
// 定位读写位置
off_t ret = lseek(fd, 5, SEEK_SET);
if (ret == -1) {
perror("lseek");
close(fd);
return errno;
}
// 读取数据
char buffer[10];
ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
if (n == -1) {
perror("read");
close(fd);
return errno;
}
buffer[n] = '\0';
printf("Data: %s\n", buffer);
// 关闭文件
close(fd);
return 0;
}
在上述示例中,我们首先使用open()
函数以只读模式打开一个文件example.txt
,并将返回的文件描述符存储在变量fd
中。如果打开文件失败,我们使用perror()
函数打印错误信息,并返回errno
变量。
接下来,我们使用lseek()
函数将读写位置定位到文件开头后的第5个字节。我们将文件描述符、偏移量和起始位置作为参数传递给lseek()
函数。如果定位读写位置失败,我们同样使用perror()
函数打印错误信息,并在关闭文件前返回errno
变量。
然后,我们使用read()
函数从文件中读取数据。我们定义一个缓冲区buffer
,并将其作为参数传递给read()
函数。read()
函数将尽量读取count
个字节的数据到缓冲区中。如果读取数据失败,我们同样使用perror()
函数打印错误信息,并在关闭文件前返回errno
变量。最后,我们在缓冲区末尾添加一个空字符,并使用printf()
函数打印读取到的数据。
最后,我们使用close()
函数关闭文件。
需要注意的是,lseek()函数可以用于定位读写位置,但并不会改变文件的大小。如果需要改变文件的大小,可以使用truncate()函数或ftruncate()函数。
stat()函数是Linux系统编程中用于获取文件信息的函数。它接受一个文件路径作为参数,并返回一个包含文件信息的结构体。
stat()函数的原型如下:
#include
#include
int stat(const char *pathname, struct stat *buf);
pathname
:要获取信息的文件路径。buf
:用于存储文件信息的结构体指针。-1
,并设置errno
变量来指示错误类型。struct stat
结构体包含了文件的各种信息,包括文件类型、权限、大小、创建时间、修改时间等。
下面是struct stat结构体的定义:
struct stat {
dev_t st_dev; // 文件所在设备的ID
ino_t st_ino; // 文件的inode号
mode_t st_mode; // 文件的类型和权限
nlink_t st_nlink; // 文件的硬链接数
uid_t st_uid; // 文件的所有者ID
gid_t st_gid; // 文件的所有者组ID
dev_t st_rdev; // 如果文件是设备文件,则为设备的ID
off_t st_size; // 文件的大小(字节)
blksize_t st_blksize; // 文件系统的块大小
blkcnt_t st_blocks; // 分配给文件的块数
time_t st_atime; // 文件的最后访问时间
time_t st_mtime; // 文件的最后修改时间
time_t st_ctime; // 文件的最后状态改变时间
};
下面是一个使用stat()函数的示例:
#include
#include
#include
#include
int main() {
const char *pathname = "example.txt";
struct stat file_info;
// 获取文件信息
int ret = stat(pathname, &file_info);
if (ret == -1) {
perror("stat");
return errno;
}
// 打印文件信息
printf("File Size: %ld bytes\n", file_info.st_size);
printf("File Permissions: %o\n", file_info.st_mode & 0777);
printf("File Owner UID: %d\n", file_info.st_uid);
printf("File Owner GID: %d\n", file_info.st_gid);
return 0;
}
在上述示例中,我们首先定义了一个文件路径pathname
和一个struct stat
结构体file_info
,用于存储获取到的文件信息。
然后,我们使用stat()
函数将文件信息存储到file_info
结构体中。我们将文件路径和file_info
结构体指针作为参数传递给stat()
函数。如果获取文件信息失败,我们使用perror()
函数打印错误信息,并返回errno
变量。
最后,我们使用printf()
函数打印获取到的文件信息,包括文件大小、文件权限、文件所有者的UID
和GID
等。
需要注意的是,stat()函数只能获取文件的信息,而不能修改文件的信息。如果需要修改文件的信息,可以使用chmod()函数来修改文件的权限。
fcntl()函数是Linux系统编程中用于对文件描述符进行控制操作的函数。它可以用于设置文件状态标志、获取文件状态标志、设置文件锁等。
fcntl()函数的原型如下:
#include
int fcntl(int fd, int cmd, ... /* arg */ );
fd
:文件描述符,可以是打开文件的文件描述符,也可以是套接字的文件描述符。cmd
:控制命令,用于指定要执行的操作。arg
:可选参数,用于传递特定操作的参数。-1
,并设置errno
变量来指示错误类型。下面是fcntl()函数的一些常用命令:
F_DUPFD
:复制文件描述符,创建一个新的文件描述符,该描述符与原始描述符指向相同的打开文件。F_GETFD
:获取文件描述符的文件状态标志。F_SETFD
:设置文件描述符的文件状态标志。F_GETFL
:获取文件的打开方式和状态标志。F_SETFL
:设置文件的打开方式和状态标志。F_GETLK
:获取文件锁的信息。F_SETLK
:设置文件锁。F_SETLKW
:设置文件锁,如果无法获取锁,则阻塞等待。下面是一个使用fcntl()函数的示例:
#include
#include
#include
int main() {
int fd = open("example.txt", O_RDWR);
if (fd == -1) {
perror("open");
return errno;
}
// 获取文件的打开方式和状态标志
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
perror("fcntl");
return errno;
}
// 设置文件的状态标志为非阻塞
flags |= O_NONBLOCK;
int ret = fcntl(fd, F_SETFL, flags);
if (ret == -1) {
perror("fcntl");
return errno;
}
// 关闭文件描述符
close(fd);
return 0;
}
在上述示例中,我们首先使用open()
函数打开一个文件,并将返回的文件描述符存储在变量fd中。如果打开文件失败,我们使用perror()
函数打印错误信息,并返回errno
变量。
然后,我们使用fcntl()
函数获取文件的打开方式和状态标志。将文件描述符和F_GETFL
命令作为参数传递给fcntl()
函数,获取到的文件状态标志存储在变量flags
中。如果获取文件状态标志失败,我们同样使用perror()
函数打印错误信息,并返回errno
变量。
接下来,我们将文件的状态标志设置为非阻塞,通过将O_NONBLOCK
标志位与flags
进行按位或操作。然后,我们使用fcntl()
函数将修改后的状态标志设置回文件描述符。将文件描述符、F_SETFL
命令和修改后的状态标志作为参数传递给fcntl()
函数。如果设置文件状态标志失败,我们同样使用perror()
函数打印错误信息,并返回errno
变量。
最后,我们使用close()
函数关闭文件描述符,释放资源。
需要注意的是,fcntl()函数的使用非常灵活,可以根据需要进行各种操作,如复制文件描述符、获取文件锁等。在使用fcntl()函数时,需要注意错误处理,并确保文件描述符的有效性。同时,需要在不再需要使用文件描述符时及时关闭文件描述符,以释放资源并避免资源泄漏。
文件IO操作是Linux系统编程中的重要部分。通过open()
、close()
、read()
、write()
等函数,我们可以对文件进行打开、读取和写入操作。通过lseek()
函数,我们可以在文件中进行定位。而通过stat()
、fcntl()
、dup()
等函数,我们可以获取文件的状态信息,对文件描述符进行控制操作,复制文件描述符等。
除了上述的函数,还有许多其他的函数可以用于文件IO操作,如mkdir()
用于创建目录,rmdir()
用于删除目录,unlink()
用于删除文件,rename()
用于重命名文件等。