提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
Linux-文件编程
前言
总结open与fopen的区别: https://www.cnblogs.com/NickyYe/p/5497659.html
一、文件
1.文件的打开和创建
2.文件的写入
3.文件的读取
4.文件的光标移动
5.实现cp命令(复制文件)
5.1简单版cp(只复制文件)
这段代码的作用是从一个源文件读取内容并将其写入到目标文件中,从而实现简易版的cp(复制)命令。但有一些问题,比如没有处理读取或写入的错误情况,也没有考虑非文本文件的处理。
①总结:
- 命令行参数检查:程序要求两个命令行参数,源文件和目标文件。
- 打开源文件:使用
open()
打开源文件,并用lseek()
获取文件大小。 - 读取文件内容:从源文件读取数据到缓冲区
readBuf
。 - 写入目标文件:将读取到的数据从缓冲区
readBuf
中取出,然后写入目标文件。 - 资源释放:关闭文件并释放分配的内存。
②代码:
#include <sys/types.h> // 包含数据类型定义,如 ssize_t 等
#include <sys/stat.h> // 包含文件属性的结构体和常量
#include <fcntl.h> // 文件控制的常量和函数,如 open() 等
#include <stdio.h> // 标准输入输出库,用于 printf() 等
#include <unistd.h> // 包含一些 POSIX API,如 close(), read(), write() 等
#include <string.h> // 字符串处理函数,如 strlen() 等
#include <stdlib.h> // 标准库函数,如 malloc(), exit() 等
int main(int argc, char **argv) // 主函数,argc 是命令行参数的数量,argv 是命令行参数的数组
{
int fdSrc; // 定义文件描述符变量,用来表示源文件
int fdDes; // 定义文件描述符变量,用来表示目标文件
char *readBuf=NULL; // 指针变量,用于存储从源文件中读取的数据
// 检查命令行参数的个数是否为 3(即:程序名 + 源文件路径 + 目标文件路径)
if(argc != 3){
printf("pararm error\n"); // 如果参数个数不对,打印错误提示
exit(-1); // 退出程序,返回 -1,表示错误
}
// 打开源文件,第一个命令行参数 argv[1] 是源文件路径,O_RDWR 表示可读写
fdSrc = open(argv[1],O_RDWR);
// 使用 lseek() 函数找到源文件的大小,先将文件指针移动到文件末尾 (SEEK_END)
int size = lseek(fdSrc,0,SEEK_END);
// 再将文件指针重新移动到文件开头 (SEEK_SET),为后续读取数据做准备
lseek(fdSrc,0,SEEK_SET);
// 动态分配内存给 readBuf,用于存放读取的文件内容,大小为文件字节数加 8
readBuf=(char *)malloc(sizeof(char)*size + 8);
// 从源文件中读取内容到 readBuf 缓冲区,读取的字节数为文件的大小
int n_read = read(fdSrc, readBuf, size);
// 打开或创建目标文件,第二个命令行参数 argv[2] 是目标文件路径
// O_RDWR 表示可读写,O_CREAT 表示如果文件不存在则创建,O_TRUNC 表示清空文件
// 0600 表示文件权限为用户可读写
fdDes = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0600);
// 将缓冲区 readBuf 中的数据写入目标文件,写入的字节数为 readBuf 的字符串长度
int n_write = write(fdDes,readBuf,strlen(readBuf));
// 关闭源文件和目标文件的文件描述符,释放资源
close(fdSrc);
close(fdDes);
return 0; // 程序正常结束,返回 0
}
5.2完美版cp(除了复制文件,还能复制其它)
为了实现一个最完美的 cp
命令(复制文件),需要注意以下几点:
- 错误处理:每个系统调用如
open()
、read()
、write()
、close()
都可能失败,因此需要对这些操作进行错误检查。 - 适应二进制文件:不应使用字符串处理函数如
strlen()
,因为它只处理文本文件。对于二进制文件,读取和写入应基于实际读取的字节数。 - 高效的内存管理:不应该一次性分配整个文件的大小(对于大文件效率低),而是分批读取和写入。
- 文件权限:保持目标文件的权限与源文件一致。
- 资源释放:在出错的情况下,要确保及时释放所有占用的资源(如文件描述符和内存)。
关键点:
- 完整的错误处理:对
open()
、read()
、write()
、close()
等操作都进行错误检查,确保程序稳健性。 - 兼容二进制文件:没有使用字符串处理函数,如
strlen()
,完全基于读取的字节数进行操作,保证能够处理任意类型的文件。 - 分块读取:使用 4KB 大小的缓冲区分块读取和写入文件,以便处理大文件,避免占用过多内存。
- 文件权限保持:目标文件的权限与源文件一致。
代码:
#include <sys/types.h> // 包含一些系统数据类型
#include <sys/stat.h> // 包含文件属性的结构和常量
#include <fcntl.h> // 文件控制的常量和函数,如 open()
#include <stdio.h> // 标准输入输出库,用于 printf()
#include <unistd.h> // 包含 POSIX API,如 read(), write(), close()
#include <stdlib.h> // 标准库函数,如 malloc(), exit()
#include <errno.h> // 错误号,用于错误处理
#include <string.h> // 字符串处理函数,如 strerror()
#define BUF_SIZE 4096 // 定义缓冲区大小为 4096 字节,方便分批读取文件
int main(int argc, char **argv) {
int fdSrc, fdDes; // 文件描述符,用于源文件和目标文件
ssize_t n_read, n_write; // 用于存储每次读取和写入的字节数
char buf[BUF_SIZE]; // 定义缓冲区,用于存储从源文件读取的数据
struct stat statbuf; // 文件属性结构体,用于获取源文件的权限信息
// 1. 参数检查:必须提供源文件路径和目标文件路径
if (argc != 3) {
fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
exit(EXIT_FAILURE); // 如果参数错误,退出程序并返回失败状态
}
// 2. 打开源文件,O_RDONLY 表示以只读模式打开
fdSrc = open(argv[1], O_RDONLY);
if (fdSrc == -1) {
// 如果打开失败,打印错误并退出程序
fprintf(stderr, "Error opening source file '%s': %s\n", argv[1], strerror(errno));
exit(EXIT_FAILURE);
}
// 3. 获取源文件的权限信息(包括文件类型、权限位等)
if (fstat(fdSrc, &statbuf) == -1) {
fprintf(stderr, "Error getting file status of '%s': %s\n", argv[1], strerror(errno));
close(fdSrc); // 关闭源文件后退出
exit(EXIT_FAILURE);
}
// 4. 打开或创建目标文件,保持源文件的权限(使用 statbuf.st_mode)
fdDes = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode);
if (fdDes == -1) {
// 如果打开失败,打印错误并关闭源文件
fprintf(stderr, "Error opening destination file '%s': %s\n", argv[2], strerror(errno));
close(fdSrc);
exit(EXIT_FAILURE);
}
// 5. 循环读取源文件并写入目标文件,直到源文件读取完毕
while ((n_read = read(fdSrc, buf, BUF_SIZE)) > 0) {
// 注意:需要确保所有读取的字节都写入目标文件
ssize_t total_written = 0;
while (total_written < n_read) {
n_write = write(fdDes, buf + total_written, n_read - total_written);
if (n_write == -1) {
// 如果写入出错,打印错误并清理资源后退出
fprintf(stderr, "Error writing to destination file '%s': %s\n", argv[2], strerror(errno));
close(fdSrc);
close(fdDes);
exit(EXIT_FAILURE);
}
total_written += n_write; // 更新已写入字节数
}
}
// 6. 检查 read() 函数是否因错误而终止
if (n_read == -1) {
fprintf(stderr, "Error reading from source file '%s': %s\n", argv[1], strerror(errno));
close(fdSrc);
close(fdDes);
exit(EXIT_FAILURE);
}
// 7. 关闭源文件和目标文件
if (close(fdSrc) == -1) {
fprintf(stderr, "Error closing source file '%s': %s\n", argv[1], strerror(errno));
exit(EXIT_FAILURE);
}
if (close(fdDes) == -1) {
fprintf(stderr, "Error closing destination file '%s': %s\n", argv[2], strerror(errno));
exit(EXIT_FAILURE);
}
// 程序成功执行,返回 0
return 0;
}
详细大白话解释代码:
-
头文件:
#include <sys/types.h>
和其他头文件用来包含必要的系统函数和数据类型。#include <errno.h>
用于获取最近一次系统调用失败的错误代码。
-
定义缓冲区:
#define BUF_SIZE 4096
定义了缓冲区大小为 4096 字节(4KB),通过分块读取文件内容,适合处理大文件。 -
参数检查:
- 程序要求 3 个命令行参数:程序名、源文件路径、目标文件路径。如果不满足条件,打印使用说明并退出。
-
打开源文件:
- 使用
open()
以只读模式打开源文件 (O_RDONLY
),并检查是否成功。 - 如果打开失败,打印错误信息(使用
strerror(errno)
来获取具体的错误描述)。
- 使用
-
获取文件属性:
fstat()
获取源文件的属性,如文件权限。用来设置目标文件的权限,使其与源文件一致。
-
打开或创建目标文件:
- 使用
open()
以可写模式打开目标文件。如果文件不存在则创建 (O_CREAT
),如果文件存在则清空 (O_TRUNC
)。 - 文件权限与源文件一致 (
statbuf.st_mode
)。
- 使用
-
循环读取和写入:
- 使用
read()
函数从源文件中读取内容到缓冲区buf
,每次最多读取BUF_SIZE
字节。 - 如果读取的字节数大于 0,则进入写入过程。写入时确保所有读取的字节都写入目标文件,使用循环处理可能的部分写入情况。
- 如果
write()
出错,打印错误信息并清理资源。
- 使用
-
错误检查:
- 在每次读取后,检查返回值是否为
-1
来判断读取是否出错。如果出错,打印错误并退出。
- 在每次读取后,检查返回值是否为
-
关闭文件:
- 使用
close()
关闭源文件和目标文件,并检查是否成功。
- 使用
-
返回值:
- 程序正常结束返回
0
,表示成功。
- 程序正常结束返回
二、进程
父进程与子进程(创建进程发生了什么事)(面)
父进程和子进程在创建进程的过程中发生了什么
cp src.c des.c
复制src.c文件到des.c
步骤:
①打开src.c
②讲src.c文件的内容读到buf中
③打开创建des.c
④将buf的内容写到des.c中
⑤close关闭两个文件
main()