
在 Linux 中,创建一个新文件可以使用 creat 系统调用。它的主要功能是创建一个新的空文件,如果指定的文件已经存在,则会将其截断为零长度(即清空文件内容)。
#include <fcntl.h>
int creat(const char *pathname, mode_t mode);这些权限位可以通过按位或(|)运算组合使用,例如,S_IRUSR | S_IWUSR 表示所有者具有读写权限。
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
int main() {
int fd;
// 创建一个名为"testfile.txt"的文件,所有者具有读写权限,所属组和其他用户无权限
fd = creat("testfile.txt", S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("creat failed");
exit(EXIT_FAILURE);
}
printf("File created successfully with file descriptor: %d\n", fd);
close(fd); // 创建文件后要记得关闭文件描述符
return 0;
}
使用 creat 函数创建了一个名为 “testfile.txt” 的文件,设置所有者具有读写权限。如果创建成功,会输出文件描述符;如果失败,会通过 perror 函数打印错误信息。
open 系统调用是 Linux 中用于打开一个已存在的文件或创建一个新文件的主要系统调用。它比 creat 系统调用功能更强大,可以通过不同的标志组合实现多种操作。
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);pathname:与 creat 系统调用中的 pathname 参数相同,指向要打开或创建的文件路径名。
flags:用于指定打开文件的方式和行为,是一个整数,可以由多个标志按位或组合而成。常见的标志有:
mode:当 flags 中包含 O_CREAT 标志时,该参数用于指定新文件的权限模式,与 creat 系统调用中的 mode 参数相同。如果 flags 中不包含 O_CREAT,则该参数被忽略。
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int fd1, fd2, fd3;
// 以只读方式打开已存在的文件"testfile.txt"
fd1 = open("testfile.txt", O_RDONLY);
if (fd1 == -1) {
perror("open for read failed");
exit(EXIT_FAILURE);
}
// 以读写方式打开文件,如果文件不存在则创建,权限为所有者读写,组和其他用户只读
fd2 = open("newfile.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd2 == -1) {
perror("open for read-write and create failed");
close(fd1);
exit(EXIT_FAILURE);
}
// 以只写、追加方式打开文件,若文件不存在则创建
fd3 = open("logfile.txt", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
if (fd3 == -1) {
perror("open for write and append failed");
close(fd1);
close(fd2);
exit(EXIT_FAILURE);
}
printf("fd1: %d, fd2: %d, fd3: %d\n", fd1, fd2, fd3);
// 关闭文件描述符
close(fd1);
close(fd2);
close(fd3);
return 0;
}
展示了 open 系统调用的几种常见用法:只读打开已存在文件、读写打开并创建新文件、只写追加打开并创建新文件。
打开文件后,就可以进行读写操作了。Linux 提供了 read 和 write 系统调用来实现对文件的读写。
read 系统调用用于从已打开的文件中读取数据。
①函数原型
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);②参数说明
③ 返回值
write 系统调用用于向已打开的文件中写入数据。
①函数原型
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);②参数说明
③返回值
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd;
char write_buf[] = "Hello, Linux File Operation!";
char read_buf[1024];
ssize_t nwritten, nread;
// 以读写方式打开文件,不存在则创建
fd = open("rwtest.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
// 写入数据
nwritten = write(fd, write_buf, strlen(write_buf));
if (nwritten == -1) {
perror("write failed");
close(fd);
exit(EXIT_FAILURE);
}
printf("Wrote %zd bytes: %s\n", nwritten, write_buf);
// 将文件指针移到文件开头(否则读取不到刚写入的数据)
off_t offset = lseek(fd, 0, SEEK_SET);
if (offset == -1) {
perror("lseek failed");
close(fd);
exit(EXIT_FAILURE);
}
// 读取数据
nread = read(fd, read_buf, sizeof(read_buf) - 1); // 留一个字节给'\0'
if (nread == -1) {
perror("read failed");
close(fd);
exit(EXIT_FAILURE);
} else if (nread == 0) {
printf("Reached end of file\n");
} else {
read_buf[nread] = '\0'; // 加上字符串结束符
printf("Read %zd bytes: %s\n", nread, read_buf);
}
close(fd);
return 0;
}
先向文件写入一段字符串,然后使用 lseek 系统调用将文件指针移到文件开头,再读取文件内容并打印。需要注意的是,write 调用返回的实际写入字节数可能小于请求的字节数,因此在实际应用中可能需要循环写入,直到所有数据都被写入。同样,read 调用也可能需要循环读取,直到读取到预期的数据量或到达文件末尾。
在对文件进行读写操作时,系统会维护一个文件偏移量(file offset),也称为文件指针,它指示了下一次读写操作开始的位置。lseek 系统调用用于修改这个文件偏移量,实现对文件的随机访问。
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd;
char buf[100];
off_t offset;
// 创建并打开一个文件
fd = open("seektest.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
// 写入一些数据
const char *data = "0123456789abcdefghijklmnopqrstuvwxyz";
write(fd, data, strlen(data));
// 将文件指针移到开头
offset = lseek(fd, 0, SEEK_SET);
if (offset == -1) {
perror("lseek to beginning failed");
close(fd);
exit(EXIT_FAILURE);
}
printf("Moved to beginning, offset: %ld\n", (long)offset);
// 读取5个字节
ssize_t nread = read(fd, buf, 5);
if (nread == -1) {
perror("read failed");
close(fd);
exit(EXIT_FAILURE);
}
buf[nread] = '\0';
printf("Read %zd bytes: %s\n", nread, buf);
// 从当前位置向后移动10个字节
offset = lseek(fd, 10, SEEK_CUR);
if (offset == -1) {
perror("lseek from current failed");
close(fd);
exit(EXIT_FAILURE);
}
printf("Moved 10 bytes from current, new offset: %ld\n", (long)offset);
// 读取5个字节
nread = read(fd, buf, 5);
if (nread == -1) {
perror("read failed");
close(fd);
exit(EXIT_FAILURE);
}
buf[nread] = '\0';
printf("Read %zd bytes: %s\n", nread, buf);
// 从文件末尾向前移动20个字节
offset = lseek(fd, -20, SEEK_END);
if (offset == -1) {
perror("lseek from end failed");
close(fd);
exit(EXIT_FAILURE);
}
printf("Moved 20 bytes before end, new offset: %ld\n", (long)offset);
// 读取5个字节
nread = read(fd, buf, 5);
if (nread == -1) {
perror("read failed");
close(fd);
exit(EXIT_FAILURE);
}
buf[nread] = '\0';
printf("Read %zd bytes: %s\n", nread, buf);
close(fd);
return 0;
}
展示了 lseek 系统调用的三种使用方式:从文件开头设置偏移量、从当前位置调整偏移量、从文件末尾调整偏移量。通过 lseek,我们可以灵活地定位到文件的任意位置进行读写操作,实现随机访问。
当对文件的操作完成后,需要使用 close 系统调用来关闭文件,释放与该文件相关的资源,如文件描述符等。
#include <unistd.h>
int close(int fd);#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int fd;
fd = open("closetest.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
// 对文件进行一些操作...
// 关闭文件
if (close(fd) == -1) {
perror("close failed");
exit(EXIT_FAILURE);
}
printf("File closed successfully\n");
return 0;
}
这个示例比较简单,展示了打开文件后进行一些操作,然后关闭文件的过程。在实际应用中,无论对文件的操作是否成功,都应该在适当的时候关闭文件。
在实际使用这些系统调用时,需要注意以下几点:
随着 Linux 系统的不断发展,文件操作的方式和接口也在不断完善。但这些基础的系统调用仍然是理解 Linux 文件系统和进行底层开发的关键。未来,我们可以进一步学习 Linux 文件系统的底层原理,以及如何利用这些系统调用来实现更复杂的文件操作功能,如文件复制、移动、权限修改等。同时,也可以学习更高层次的文件操作库函数(如标准 C 库中的 fopen、fread、fwrite 等),它们是对这些系统调用的封装,使用起来更加方便,但了解其底层实现的系统调用有助于我们更好地理解和使用这些库函数。
通过不断学习和实践,我们可以更深入地掌握 Linux 文件操作技术,为开发高效、稳定的 Linux 应用程序打下坚实的基础。