【Linux】文件编程

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

总结open与fopen的区别: https://www.cnblogs.com/NickyYe/p/5497659.html


一、文件

1.文件的打开和创建

2.文件的写入

3.文件的读取

4.文件的光标移动

5.实现cp命令(复制文件)

5.1简单版cp(只复制文件)

这段代码的作用是从一个源文件读取内容并将其写入到目标文件中,从而实现简易版的cp(复制)命令。但有一些问题,比如没有处理读取或写入的错误情况,也没有考虑非文本文件的处理。

①总结:
  1. 命令行参数检查:程序要求两个命令行参数,源文件和目标文件。
  2. 打开源文件:使用 open() 打开源文件,并用 lseek() 获取文件大小。
  3. 读取文件内容:从源文件读取数据到缓冲区 readBuf
  4. 写入目标文件:将读取到的数据从缓冲区 readBuf中取出,然后写入目标文件。
  5. 资源释放:关闭文件并释放分配的内存。
②代码:
#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 命令(复制文件),需要注意以下几点:

  1. 错误处理:每个系统调用如 open()read()write()close() 都可能失败,因此需要对这些操作进行错误检查。
  2. 适应二进制文件:不应使用字符串处理函数如 strlen(),因为它只处理文本文件。对于二进制文件,读取和写入应基于实际读取的字节数。
  3. 高效的内存管理:不应该一次性分配整个文件的大小(对于大文件效率低),而是分批读取和写入。
  4. 文件权限:保持目标文件的权限与源文件一致。
  5. 资源释放:在出错的情况下,要确保及时释放所有占用的资源(如文件描述符和内存)。

关键点:

  • 完整的错误处理:对 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;
}

详细大白话解释代码:

  1. 头文件

    • #include <sys/types.h> 和其他头文件用来包含必要的系统函数和数据类型。
    • #include <errno.h> 用于获取最近一次系统调用失败的错误代码。
  2. 定义缓冲区#define BUF_SIZE 4096 定义了缓冲区大小为 4096 字节(4KB),通过分块读取文件内容,适合处理大文件。

  3. 参数检查

    • 程序要求 3 个命令行参数:程序名、源文件路径、目标文件路径。如果不满足条件,打印使用说明并退出。
  4. 打开源文件

    • 使用 open() 以只读模式打开源文件 (O_RDONLY),并检查是否成功。
    • 如果打开失败,打印错误信息(使用 strerror(errno) 来获取具体的错误描述)。
  5. 获取文件属性

    • fstat() 获取源文件的属性,如文件权限。用来设置目标文件的权限,使其与源文件一致。
  6. 打开或创建目标文件

    • 使用 open() 以可写模式打开目标文件。如果文件不存在则创建 (O_CREAT),如果文件存在则清空 (O_TRUNC)。
    • 文件权限与源文件一致 (statbuf.st_mode)。
  7. 循环读取和写入

    • 使用 read() 函数从源文件中读取内容到缓冲区 buf,每次最多读取 BUF_SIZE 字节。
    • 如果读取的字节数大于 0,则进入写入过程。写入时确保所有读取的字节都写入目标文件,使用循环处理可能的部分写入情况。
    • 如果 write() 出错,打印错误信息并清理资源。
  8. 错误检查

    • 在每次读取后,检查返回值是否为 -1 来判断读取是否出错。如果出错,打印错误并退出。
  9. 关闭文件

    • 使用 close() 关闭源文件和目标文件,并检查是否成功。
  10. 返回值

    • 程序正常结束返回 0,表示成功。

二、进程

父进程与子进程(创建进程发生了什么事)(面)

父进程和子进程在创建进程的过程中发生了什么在这里插入图片描述

cp src.c des.c
复制src.c文件到des.c
步骤:
①打开src.c
②讲src.c文件的内容读到buf中
③打开创建des.c
④将buf的内容写到des.c中
⑤close关闭两个文件

main()

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值