C语言---14文件操作---01文件内容的顺序读写

本文详细介绍了C语言中文件的基本概念、分类及操作方法,包括文件的打开与关闭、字符与字符串读写、数据块读写及格式化操作等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文中的源码都在这里哦!!!

文中的源码都在这里哦!!!

一、文件的基本概念

  • 一个文件通常是磁盘上一段命名的存储区

  • 磁盘文件(通常用的文件):指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上,使用时才调入内存

  • 设备文件(与硬件有关的文件):在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写。例如:键盘(标准输入文件)、屏幕(标准输出文件)

  • 以下说的是磁盘文件

(一)磁盘文件的存取过程

  • 磁盘文件,一般保存在硬盘、光盘、U盘等掉电不丢失的磁盘设备中,在需要时调入内存

  • 在内存中对文件进行编辑处理后,保存到磁盘中(内存只是文件的处理区,不是存储区)

  • 程序与磁盘之间交互,不是立即完成,系统或程序可根据需要设置缓冲区,以提高存取效率,延长磁盘寿命(在缓冲区进行暂时存储,直到存储的数据量达到一定的数量,不然存储一次打开一次磁盘,会缩短磁盘寿命)

 

(二)文件分类(重要)

1、物理上

  • 所有的磁盘文件都是二进制存储,以字节为单位进行顺序存储(空间是连续的)

2、逻辑上

  • 程序文件:有后缀名,主要用于存储程序代码

  • 数据文件:程序运行时读写的数据

  • 数据文件可分为ASCII文件(文本文件)二进制文件——C语言可识别的文件

  • 文本文件

  • 基于字符编码,常见编码有ASCII、UNICODE等,一般可以使用文本编辑器直接打开

  • 例如:数5678以ASCII存储形式为

    • ASCII码:00110101 00110110 00110111 00111000

    • 是每个字符的ASCII码值

    • 效率低,需要查表,但便于查看

  • 二进制文件

  • 基于值编码,根据应用指定某个值的意思

  • 把内存中的数据按其在内存中的存储形式原样输出到磁盘上

  • 例如:5678的存储形式为

    • 进制码:0001 0110 0010 1110

    • 读取效率很高,但是解译很麻烦,查看麻烦

3、两者的对比

文本文件二进制文件
译码编码基于字符定长,译码容易些编码是变长的,译码难一些(不同的二进制文件格式,有不同的译码方式)
空间利用率任何一个符号至少需要一个字节用一个比特来代表一个意思(位操作)
可读性通用的记事本工具就几乎可以浏览所有文本文件需要一个具体的文件解码器

二、文件操作

  • C语言中不能直接操作文件,需要借助库函数对文件进行操作

  • 基本流程

  • 在使用文件前要调用打开函数将文件打开,得到文件指针fp

  • 然后调用各种有关函数,利用fp对文件进行具体处理(读或写)

  • 在文件用完时,及时调用关闭函数来关闭文件

  • C语言中所有的文件操作都围绕文件指针完成(只有通过文件指针,我们才能知道自己操作的是哪个文件),所以操作文件前必须定义一个文件指针指向我们将要操作的文件

(一)文件指针

  • 文件指针:指向文件的指针,可以指向文件的内存地址

  • 格式:FILE * 指针变量标识符

 // 由于文件类型已经在stdio.h头文件中有声明了,所以我们不需要另外声明,直接使用就行了
 FILE *fp = NULL;
  • FILE为大写,需要包含头文件<stdio.h>

  • FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型,FILE结构体中含有文件名、文件状态和文件当前位置等信息(不需要关心)

  • c语言中有三个特殊的文件指针无需定义,打开可直接使用(在后续说明中会提到!!!)

  • stdin:标准输入。默认为当前终端(键盘),使用的scanf、getchar函数默认从此终端获得数据

  • stdout:标准输出。默认为当前终端(屏幕)使用的printf、puts函数默认输出信息到此终端

  • stderr:标准出错。默认为当前终端(屏幕)当我们程序出错或者使用perror函数时信息打印在此终端

 

(二)打开与关闭文件

  • 任何文件使用之前必须打开,使用后必须关闭

1、fopen打开一个文件

  • 格式:fp = fopen(文件名,文件打开方式)

  • 返回值:返回的是文件的起始地址,执行不成功返回NULL(需要在打开之后判断是否打开成功)

  • 文件名:要操作文件的名字,可包含路径

  • 文件路径 + 文件名主干 + 文件后缀

  • 文件的路径可以唯一标识文件的位置

  • 绝对路径:从根目录(盘符)开始的文件路径 + 文件名 + 文件扩展名(文件类型)

  • 相对路径:相对于当前目录,从当前目录开始访问到某一文件的路径

  • 打开方式:读、写、二进制、文本(与文件的存储方式无关,与操作系统有关)

  • r:以只读方式打开文件。文件不存在返回NULL;文件存在返回文件指针,进行后续的读操作

  • w:以只写方式打开文件。文件不存在指定文件名创建此文件文件存在则清空文件内容,进行写操作;文件打不开,返回NULL

  • a:以追加方式打开文件。文件不存在,以指定文件名创建此文件(同w);若文件存在,从文件的结尾处进行写操作

  • +:可读可写

  • b:以二进制的方式打开文件

  • t:以文本的方式打开文件(省略!!!)

  • 不是很重要,了解一下就行 
  • 在windows平台下,当读取文件的时候,系统会将所有的"\r\n"转换成"\n";当写入文件的时候,系统会将"\n"转换成"\r\n"写入
  • 在Unix/Linux平台下,“文本”与“二进制”模式没有区别。\r\n作为两个字符原样输入输出
  • 打开方式的组合形式 

模  式

功   能

r或rb

以只读方式打开一个文本文件(不创建文件)

w或wb

以写方式打开文件(使文件长度截断为0字节,创建一个文件)

a或ab

以添加方式打开文件,即在末尾添加内容,当文件不存在时,创建文件用于写

r+或rb+

以可读、可写的方式打开文件(不创建新文件)

w+或wb+

以可读、可写的方式打开文件

(使文件长度为0字节,创建一个文件)

a+或ab+

以添加方式打开文件,打开文件并在末尾更改文件(如果文件不存在,则创建文件)

  • 顺序读写文件:文件指针会从文件指针当前位置顺序读写文件,打开文件时,文件指针默认指向文件的开头 

2、fclose关闭文件

  • 格式:fclose(文件指针);

  • 正常执行则返回0,不成功则返回-1(EOF)

3、练习

  • 以只读、文本方式打开当前路径下一个叫test.txt文件

  • 以只写、二进制方式打开同一个文件

  • 以追加(a)、文本的方式打开同一个文件

  • 同时以读/写、二进制方式打开同一个文件,要求若文件不存在,提示出错

  • 同时以读/些、文本方式打开同一个文件,要求若文件不存在,创建此文件

  • 若打开成功则关闭相应的文件

void test1() 
{
    FILE *fp = NULL; 
    fp = fopen("tset.txt", "r"); // 只读、文本
    fp = fopen("test.txt","wb"); // 只写、二进制
    fp = fopen("test.txt","a"); // 追加、文本
    fp = fopen("tset.txt", "rb+"); // 可读可写、二进制、不存在报错
    fp = fopen("test.txt","w+"); // 可读可写、文本、不存在创建

    if(fp != NULL) // 打开成功则关闭相应的文件
    {
        fclose(fp); 
    }
}

  • 对文件操作最常用的是:“读”和“写”

  • C语言提供了多种对文件读写的函数

  • 字节读写函数:  fgetcfputc

  • 字符串读写函数:fgetsfputs

  • 数据块读写函数:freadfwrite

  • 格式化读写函数:fscanffprintf

  • 以上函数可完成对文件内容的顺序读写


 

(三)文件的字节读写(单个字符的输入输出)

1、fgetc:字节读操作

  • 格式:ch = fgetc(fp); 

  • 说明

  • 从指定文件读取一个字节赋给ch(以“读”或“读写的方式”打开文件)

  • 读取文本文件,读到文件结尾返回EOFEOF是在stdio.h文件中定义的符号常量,值为-1)

  • 二进制文件,读到文件结尾,使用feof判断结尾(后面会讲)

2、读操作示例

  • 如果本地没有创建相应的文件,会报错如下所示

  • 事先在本地创建a.txt,文件内容为 hello file

  • 文件输出:buf = hello file

void test2()
{
    char buf[128] = "";
    int i = 0;
    FILE* fp = NULL;

    // 1、使用fopen打开一个文件,获得文件指针
    fp = fopen("a.txt", "r");
    if (fp == NULL) // 以只读的形式打开文本,当前目录下没有该文本,输出错误信息,然后退出
    {
        perror("fopen");
        return;
    }

    // 2、对文件的操作fgetc
    while (1)
    {
        // fgetc调用一次 读取到一个字节
        buf[i] = fgetc(fp);
        if (buf[i] == EOF) // EOF表示已经到文件末尾
        {
            break;
        }
        i++;
    }
    printf("buf = %s\n", buf);

    // 3、关闭文件
    fclose(fp);
}

3、fputc:字节的写操作

  • 格式:fputc(ch,fp);

  • 说明

  • 把一个ch变量中的值(1个字节写到指定的文件

  • 如果输出成功,则返回输出的字节;如果输出失败,则返回一个EOF

4、写操作示例

void test3()
{
    char buf[128] = "";
    int i = 0;
    FILE* fp = NULL;
    fp = fopen("b.txt", "w"); // 没有就创建
    if (fp == NULL)
    {
        perror("fopen");
        return;
    }

    // 使用fputc进行文件的数据写入
    printf("请输入要写入文件的字符串:");
    fgets(buf, sizeof(buf), stdin); // 会获取换行符
    buf[strlen(buf) - 1] = 0; // 去掉键盘输入的换行符

    // 将字符串buf中的元素 逐个写入文件中
    while (buf[i] != '\0')
    {
        fputc(buf[i], fp);
        i++;
    }
    fclose(fp);
}

5、练习:从a.txt读取文件内容写入到b.txt

void test4()
{
    FILE *fp1 =NULL;
    FILE *fp2 =NULL;
    
    // 以只读的方式打开a.txt
    fp1 = fopen("a.txt","r");
    if(fp1 == NULL)
    {
        perror("fopen");
        return;
    }
    
    // 以只写的方式打开b.txt
    fp2 = fopen("b.txt","w");
    if(fp2 == NULL)
    {
        perror("fopen");
        return;
    }
    
    
    // 从fp1中每读取一个字节写入到fp2中
    while(1)
    {
        char ch;
        // 读
        ch = fgetc(fp1);
        if(ch == EOF) // 已经读到文件末尾
            break;
        
        // 写
        fputc(ch,fp2);
        
    }

    fclose(fp1);
    fclose(fp2);
    return;
}

 

(四)字符串的输入输出(文件的字符串读写)

1、fputs:往文件中写入一个字符串

  • 格式:fputs("china", fp);

  • 说明

  • 向指定文件写入一个字符串

  • 第一个参数可以是字符串常量、字符数组、字符指针

  • 字符串末尾的'\0'不会写进文件

void test5()
{
    // 指针数组
    char *buf[]={"窗前明月光\n","疑似地上霜\n","举头望明月\n","低头思故乡"};
    int n = sizeof(buf)/sizeof(buf[0]);
    FILE *fp = NULL;
    int i=0;

    fp = fopen("c.txt", "w");
    if(fp == NULL)
    {
        perror("fopen");
        return;
    }

    for(i=0;i<n; i++)
    {
        fputs(buf[i], fp);
    }

    fclose(fp);
}

2、fgets:从文件中获取字符串

  • 格式:fgets(str, n, fp);

  • 说明

  • 功能:从fp指向的文件中读取n-1个字符,在读取n-1个字符之前遇到换行符或EOF,提前结束读取并在最后加一个’\0’

  • str:存放数据的首地址

  • 返回值:成功读取则返回读到字符串的首元素地址,失败则返回NULL

  • 可以读取换行符

  • 用来获取文件一行的数据

void test6()
{
    char buf[128] = "";
    FILE* fp = NULL;
    char* path = "c.txt"; // path指向文件名为"c.txt"的地址
    fp = fopen(path, "r");
    if (fp == NULL)
    {
        perror("fopen");
        return;
    }

    while (fgets(buf, sizeof(buf), fp))
    {
        printf("%s\n", buf);
    }
    fclose(fp);
}

3、综合示例

void test7()
{
    FILE* fp = NULL;
    char str[10] = "\0";
    fp = fopen("test1.txt", "w");
    fputs("XYZ123*#!", fp); // 将字符串"XYZ123*#!"写入文件指针fp指向的当前文件位置
    fclose(fp);

    fp = fopen("test1.txt", "r+");
    fgets(str, 8, fp); // 从文件指针fp指向文件的当前位置读取7个字符存入字符数组str中
    // 获取字符的长度会比参数给定的长度小1个,因为需要用于存储'\0'
    puts(str);
    fclose(fp);
}

4、练习:从一个文件中读取一个字符串,输出到另一个文件

void test8()
{
    FILE* fp_r, * fp_w; // 文件读写指针
    char str[100] = "";
 
    fp_r = fopen("src.text", "r+");
    if (fp_r == NULL) // 判断文件是否正常打开
    {
        perror(fopen);
        return;
    }
    fp_w = fopen("dest.text", "w+");
    if (fp_w == NULL)
    {
        perror(fopen);
        return;
    }

    // 文本输入输出
    fgets(str, 20, fp_r);
    puts(str);
    fputs(str, fp_w);

    // 键盘输入,屏幕输出
    fgets(str, 100, stdin);
    fputs(str, stdout);

    fclose(fp_r);
    fclose(fp_w);
    return 0;
}

 

(五) 文件块的读写(以二进制的形式读写数据)

1、fwrite:将数据块写入到文件中

  • 格式:fwrite(buffer,size,count,fp);

  • 说明

  • buffer:指向存储数据空间的首地址的指针

  • size:一次性读取数据块大小

  • count:要读取的数据块的个数

  • fp:指向要进行写操作的文件指针

  • 返回实际读写的数据块数---count(不是总数据大小哦!!!)

void test08()
{
    // 定义一个结构体,英雄名、喜爱程度、玩的次数
    HERO hero[] = {
        {"盾山",100, 150},
        {"牛魔",80, 20},
        {"鲁班大师",95, 85},
        {"东皇",100, 90}
    };
    int n = sizeof(hero) / sizeof(hero[0]);

    FILE* fp = NULL;
    fp = fopen("hero.txt", "w");
    if (fp == NULL)
    {
        perror("fopen");
        return;
    }
    // fwrite将内存的数据原样的输出到文件中
    // 写入文件的数据不便于用户查看,但是不会影响程序的读
    fwrite(hero, sizeof(HERO), n, fp);

    fclose(fp);
}
  • 查看的时候发现和自己写入的内容不一样,这就是文件块的特点保存的快,但是查看比较麻烦 

2、fread:从文件中读取数据块

  • 格式:fread(buffer,size,count,fp);

  • 参数与fwrite一样

void test10()
{
    HERO hero[4];
    int i = 0;
    FILE* fp = NULL;
    fp = fopen("hero.txt", "r");
    if (fp == NULL)
    {
        perror("fopen");
        return;
    }

    fread(hero, sizeof(HERO), 4, fp); // 结构体数组中有四组数据
    for (i = 0; i < 4; i++)
    {
        printf("英雄姓名:《%s》,喜爱程度:《%d》,玩的次数:《%d》\n", \
            (hero + i) -> name, (hero + i) ->Is_Love, (hero + i) ->Play_NUm);
    }
    fclose(fp);
}

3、综合示例

void test11()
{
    FILE* fp = NULL;
    int arr[6] = { 1, 2, 3, 4, 5, 6 };
    fp = fopen("test3.txt", "wb"); // 只写的形式打开二进制文本
    fwrite(arr, sizeof(int), 6, fp); // 把整型数组arr中6个大小为int类型的数据写入文件指针fp所指向的文件当前位置
    fclose(fp);

    int arr2[6] = { 0 };
    fp = fopen("test3.txt", "rb");
    fread(arr2, sizeof(int), 6, fp); // 从文件指针fp所指向文件的当前位置读取6个整型大小的数据放入数组arr2中
    for (int i = 0; i < 6; i++)
        printf("%d\t", arr2[i]);
    putchar('\n');
    fclose(fp);
}

 

4、练习:从键盘输入一个结构体数组数据,输出到文件,再读入并显示

void test12()
{
    struct stu boya[10], boyb[2];
    FILE* fp = NULL;
    int i = 0;
    fp = fopen("test12.txt", "wb+");
    if (fp == NULL)
    {
        perror(fopen);
        return;
    }
    printf("请输入数据:\n");
    for (; i < 2; i++)
    {
        scanf("%s %d %d", boya[i].name, &boya[i].num, &boya[i].age);
    }
    fwrite(boya, sizeof(boya), 2, fp); // 学生信息写入文件
    rewind(fp); // 重新将文件指针指向开头
    fread(boyb, sizeof(boyb), 2, fp);
    for (i = 0; i < 2; i++)
    {
        printf("%s %d %d", boyb[i].name, boyb[i].num, boyb[i].age);
    }
    fclose(fp);
}

(六)文件的格式化操作(格式化输入和输出函数)

  • 格式化输入输出:以什么格式输入的就以什么格式输出

  • 针对文件块查看不便的问题,通过格式化输入输出的方法

1、fprintf:格式化写操作

  • 格式:fprintf(文件指针, 格式化字符串, 输出列表);

void test13()
{
    HERO hero[] = {
        {"盾山",100, 150},
        {"牛魔",80, 20},
        {"鲁班大师",95, 85},
        {"东皇",100, 90}
    };
    int n = sizeof(hero) / sizeof(hero[0]);
    FILE* fp = NULL;
    int i = 0;

    fp = fopen("hero.txt", "w");
    if (fp == NULL)
    {
        perror("fopen");
        return;
    }

    for (i = 0; i < n; i++)
    {
        fprintf(fp, "英雄:%s 喜爱程度:%d 玩的次数:%d\n", hero[i].name, hero[i].Is_Love, hero[i].Play_NUm);
    }
    fclose(fp);
}

 

2、fscanf:格式化读操作

  • 格式:fscanf(文件指针, 格式化字符串, 输入列表);

void test14()
{
    HERO hero[4];
    int i = 0;
    FILE* fp = NULL;
    fp = fopen("hero.txt", "r");
    if (fp == NULL)
    {
        perror("fopen");
        return;
    }

    for (i = 0; i < 4; i++)
    {
        fscanf(fp, "英雄:%s 喜爱程度:%d 玩的次数:%d\n", hero[i].name, &hero[i].Is_Love, &hero[i].Play_NUm);
    }

    for (i = 0; i < 4; i++)
    {
        printf("%s %d %d\n", hero[i].name, hero[i].Is_Love, hero[i].Play_NUm);
    }
    fclose(fp);
}

 

3、综合示例

  • 优点:对磁盘文件读写使用方便,容易理解,直观的查看

  • 缺点:在输入时将ASCII码转换成二进制,在输出时将二进制转为字符,花费时间较多

struct student { // 学生结构体
    int id;
    char name[20];
    char sex[4];
    int age;
    float score;
};
void test15()
{
    FILE* fp = NULL;
    struct student S1 = { 1, "小明", "男", 18, 90.5 }, S2;
    fp = fopen("test15.txt", "w");
    fprintf(fp, "%d %s %s %d %f\n", S1.id, S1.name, S1.sex, S1.age, S1.score); // 以指定的格式把数据写入文件指针fp所指向的文件中
    fclose(fp);
    fp = fopen("test15.txt", "r");
    fscanf(fp, "%d %s %s %d %f\n", &S2.id, S2.name, S2.sex, &S2.age, &S2.score); // 以指定的格式从文件指针fp所指向的文件中读取一些数据放入结构体变量S2中
    fclose(fp);
}

 

 

 (七)格式化输入输出与文件块对比

  • 文件拷贝用文件块read和fwrite函数,速度快,查看不方便

  • 文件打印输出查看用fprintf和fcsanf函数,不在乎时间,需要的是直观可看

  • 在内存与磁盘频繁交换数据的情况下,最好不用fprintf和fcsanf函数,而使用fread和fwrite函数

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盾山狂热粉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值