文件操作的学习及练习

一、文件基础概念
  1. 文件分类

    分类维度具体类型说明
    逻辑结构文本文件、二进制文件数据存储形式
    存储介质磁盘文件、光盘文件、内存文件物理存储载体
    数据组成形式顺序文件、索引文件、链式文件数据组织方式
  2. 文件名组成

    [文件路径] + [文件名主干] + [文件后缀]
    示例:C:\data\example.txt
    - 路径:C:\data\     主干:example     后缀:.txt
    

二、文件系统与操作
  1. 文件指针与操作流程

    FILE *pf; // 指向文件信息区的指针
    pf = fopen("filename", "mode"); // 打开文件
    if (pf == NULL) {
        perror("Error"); // 错误处理
        return 1;
    }
    // 文件读写操作...
    fclose(pf); // 关闭文件(成功返回0,失败返回EOF)
    pf = NULL;  // 避免野指针
    

    文件打开模式 (mode 参数)

    模式功能
    r只读,文件必须存在
    w只写,创建新文件或清空现有文件
    a追加,写入数据到文件末尾
    rb二进制读模式
  2. 路径类型

    类型示例说明
    绝对路径C:\data\file.txt完整路径,从根目录开始
    相对路径../src/main.c相对于当前工作目录

三、文件读写操作
  1. 字符级读写

    // 字符写入
    fputc('A', pf); // 成功返回写入字符,失败返回EOF
    // 字符读取
    int ch = fgetc(pf); // 使用int接收(兼容EOF=-1)
    
  2. 行级读写

    // 读取一行
    char buffer[100];
    fgets(buffer, 100, pf);
    // 写入一行
    fputs("Hello World", pf);
    
  3. 二进制读写

    // 写入结构体
    struct Student stu = { "Alice", 20 };
    fwrite(&stu, sizeof(struct Student), 1, pf);
    // 读取结构体
    fread(&stu, sizeof(struct Student), 1, pf);
    

四、示例
1.带命令行参数的main函数
int main(int argc, char *argv[]) {
    // 代码逻辑
    return 0;
}
  • 参数说明

    • argc:命令行参数个数(包括程序自身名称)。
    • argv:字符串指针数组,存储命令行参数的具体内容。
    • argv[0]:程序名称。
    • argv[1]argv[argc-1]:用户输入的参数。
1)处理命令行参数
#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("程序名称: %s\n", argv[0]);
    printf("参数个数: %d\n", argc - 1);
    
    for (int i = 1; i < argc; i++) {
        printf("参数 %d: %s\n", i, argv[i]);
    }
    return 0;
}

运行效果

$ ./program apple banana
程序名称: ./program
参数个数: 2
参数 1: apple
参数 2: banana
2)打印出命令行中传递给程序的所有参数
int main(int argc, char **argv)
{
    int i;
    for(i=0; i<argc; i++)
        printf("%s\n", argv[i]);
    return 0; // 通常返回0表示程序成功执行
}
  • 函数定义

    int main(int argc, char **argv)
    
    • main是C程序的入口点,程序从这里开始执行。
    • argcArgument Count)是一个整数,表示命令行参数的数量。
    • argvArgument Vector)是一个指向字符串数组的指针,每个字符串代表一个命令行参数。
      变量声明
    int i;
    

    声明一个整型变量i,用于循环计数。

    • 循环遍历参数
    for(i=0; i<argc; i++)
        printf("%s\n", argv[i]);
    
  • for循环从0开始,依次访问每个参数,直到i等于argc(此时已遍历完所有参数)。

  • argv[0]通常是程序本身的名称(例如./a.out)。

  • argv[1]argv[argc-1]是用户在命令行中提供的实际参数。

  • printf("%s\n", argv[i])会打印每个参数字符串,并在末尾添加换行符\n

示例
如果在命令行中执行:

./program(你的文件名) hello world 123

程序会输出:

./program
hello
world
123

以空格为分隔符,例如hello是一个字符串

3)C程序默认打开的流
输入
输出
错误
stdin
C程序
stdout
stderr
2.实现cat(显示到屏幕上)功能

将指定文件的内容逐字符读取并输出到标准输出(通常是终端)。

#include <stdio.h>

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "用法:%s <文件名>\n", argv[0]);
        return 1;
    }

    FILE *pf = fopen(argv[1], "r");
    if (pf == NULL) {
        perror("无法打开文件");
        return 1;
    }

    int ch;
    while ((ch = fgetc(pf)) != EOF) {
        fputc(ch, stdout);
    }

    fclose(pf);  // 关闭文件
    return 0;
}
1) 函数定义与参数
int main(int argc, char *argv[])
  • main 是程序的入口点。
  • argcArgument Count):命令行参数的数量(至少为1,即程序名本身)。
  • argvArgument Vector):字符串数组,存储命令行参数。
    • argv[0]:程序名(如 ./program)。
    • argv[1]:第一个参数,这里用作要读取的文件名
2)文件操作
FILE* pf = fopen(argv[1], "r");
  • fopen 函数尝试以只读模式"r")打开用户指定的文件(argv[1])。
    • 如果文件不存在或无法打开。
  • FILE* 是指向文件流的指针,用于后续操作。
3) 字符读取与输出循环
int ch = fgetc(pf);
while (ch != EOF)
{
    fputc(ch, stdout);
    ch = fgetc(pf);
}
  • fgetc(pf):从文件流 pf 中读取一个字符,并返回其 ASCII 值(类型为 int)。若到达文件末尾(End of File),返回 EOF(通常为 -1)。
  • ch != EOF:循环条件,确保未到达文件末尾。
  • fputc(ch, stdout):将字符 ch 输出到标准输出流(即屏幕)。
    • stdout 是预定义的标准输出流(类似 printf 的默认输出)。
  • 循环逻辑
    在 C 语言中,文件流(FILE*)维护了一个内部位置指针,用于记录当前读取或写入的位置。当你调用 fgetc() 函数时,它会执行以下操作:
    1. 读取当前位置的字符:从文件的当前位置读取一个字节(字符)。
    2. 移动位置指针:自动将文件位置指针向后移动1 个字符(即指向下一个字符)。
    3. 返回字符值:返回读取的字符(转换为 int 类型)。
4)字符类型问题

ch 必须是 int 类型,而非 char,以正确处理 EOFchar 无法存储 -1)。

示例运行
若编译后程序名为 copy,执行:

./copy example.txt

程序会将 example.txt 的内容逐字符复制到终端显示。

3.实现copy命令的C代码
int main(int argc, char *argv[])
{
    int i;
    if(argc<3)  // 检查命令行参数是否足够(程序名+源文件+目标文件)
    {
        printf("error!Need 3 arguments!");  // 提示需要3个参数
        return 0;  // 参数不足时退出程序
    }
    FILE* fp=fopen(argv[1],"r");  // 打开第一个参数指定的文件(源文件)用于读取
    FILE* fp2=fopen(argv[2],"w");  // 打开第二个参数指定的文件(目标文件)用于写入
    if(fp==NULL||fp2==NULL)  // 检查文件是否成功打开
    {
        printf("open or creat a file error!");  // 提示文件打开或创建失败
        return 0;  // 文件打开失败时退出程序
    }
    int ch=fgetc(fp);  // 从源文件读取第一个字符
    while(ch!=EOF)  // 循环直到文件结束(EOF)
    {
        fputc(ch,fp2);  // 将字符写入目标文件
        ch=fgetc(fp);  // 从源文件读取下一个字符
    }
    fclose(fp);  // 关闭源文件
    fclose(fp2);  // 关闭目标文件
    fp=NULL;  // 避免悬空指针
    return 0;  // 程序正常结束
}
4.用fgets和fputs写cat命令
void myCat(char *source)
{

    FILE* fp=fopen(source,"r");        // 打开源文件用于读取
    if(fp==NULL)
    {	
    	perror("无法打开文件");
    	return ;
    }
    char str[200];                     // 定义缓冲区存储每行内容
    fgets(str,200,fp);                 // 读取第一行
    while(!feof(fp))                   // 循环直到文件结束
    {
        fputs(str,stdout);             // 输出当前行到标准输出
        fgets(str,200,fp);             // 读取下一行
    }
    fclose(fp);                        // 关闭文件流
    fp=NULL;

}

!feof(fp) 是C语言中用于判断文件是否结束的条件表达式,其含义为:“文件尚未结束”。下面详细解释其工作原理和潜在问题:

1) feof() 函数的作用
  • 功能feof(FILE* stream) 检查文件流是否已到达文件末尾(End of File,简称EOF)。
  • 返回值
    • 若文件流已到达EOF,返回 非零值(通常为 1)。
    • 若文件流未到达EOF,返回 0
2) !feof(fp) 的逻辑
  • ! 是逻辑非运算符,将 feof(fp) 的结果取反:
    • 当文件未结束时,feof(fp) 返回 0!feof(fp)1(真),循环继续。
    • 当文件结束时,feof(fp) 返回 1!feof(fp)0(假),循环终止。
3)常见误区与问题

误区示例

while (!feof(fp)) {
    fgets(str, 200, fp);  // 先读取,再判断
    fputs(str, stdout);   // 若读取失败,str可能是上次的内容
}

问题分析

  1. 最后一行重复输出
    若文件最后一行没有换行符,fgets() 会读取该行并将文件指针移至EOF,但此时 !feof(fp) 仍为真,导致再次进入循环。此时 fgets() 读取失败(返回 NULL),但 str 保留上次内容,造成重复输出。

  2. 安全风险
    fgets() 读取失败(如文件损坏),str 内容未被更新,可能输出脏数据。

4) 推荐的循环写法

按字符读取

int ch;
while ((ch = fgetc(fp)) != EOF) {  // 读取并判断
    fputc(ch, stdout);
}

按行读取

char str[200];
while (fgets(str, 200, fp) != NULL)
 {  // 读取成功时循环
    fputs(str, stdout);
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值