当前位置: 首页 > article >正文

C语言自定义类型与文件操作

构造类型

枚举类型

若定义不相关的常量使用宏定义;若定义一组相关的常量使用枚举。switch中case后访问的就是枚举。

  • 定义:

    我们一般情况下定义常量使用宏定义(#define),宏定义适合没有关联关系的常量;但有时需要对一组有关联关系的量进行定义,例如:星期、月份、方向(上下左右中)等,若使用宏定义,可读性差,这时使用枚举。

  • 说明

    • 枚举类型定义了一组常量,我们在开发中直接使用这些常量。
    • 枚举类型也可以类似结构体一样定义变量操作 (不常用)
    • 枚举有默认值,从0开始依次+1;同时也可以指定它的默认值,后续的依次根据赋值+1
  • 特点:

    定义一组常量,类似于定义了多个自定义常量(宏定义)

    提供了代码的可读性

  • 语法

//定义枚举类型名以后可以定义该枚举类型的变量
enum 枚举类型名 变量列表;

//定义枚举类型的同时定义该枚举类型的变量
enum 枚举类型名{枚举元素列表} 变量列表;
    
//直接定义枚举变量
enum {枚举元素列表} 变量列表;


enum Week
{
    //定义枚举元素
    SUN = 10,MON,TUE,WED,THU,FRI,SAT
};

//访问枚举元素
printf("%d,%d,%d\n",SUN,WED,SAT);

//定义枚举类型变量(先定义,后赋值)
enum Week week;
//初始化
week = TUE;

//定义枚举变量同时赋值
enum Week week1 =THU;

typedef

  • 说明:给类型重命名,不会影响类型本身

  • 作用:给已有的类型起别名

  • 格式:

    //先定义结构体类型,再重命名
    typedef 已有类型名 新别名;
    struct Student
    {
        int id;
        char name[20];
        char sex;
    };
    
    //类型重命名
    typedef struct Student Stu;
    
    //定义结构体变量
    struct Stu stu = {1,"张三"'M'};
    
    //定义结构体的同时重命名
    typedef struct PersonInfo
    {
        int a;
        double b;
    } Per;//结构体的别名,本质上还是数据类型
    
    struct Per per = {1,5};
    
  • 应用场景:

    • 数据类型复杂(结构体、共用体、枚举、结构体指针、无符号长整形)时使用
    • 为了跨平台兼容如:
      1. size_t:类型重命名后数据类型:typedef unsigned long size_t
      2. unit_16:类型重名后数据类型

文件操作

概述
  • 什么是文件

    文件是保存在外存储器(U盘,移动硬盘)的数据的集合。

  • 文件操作体现在哪几个方面

    1. 文件内容的读取
    2. 文件内容的写入

    数据的读取和写入可被视为针对文件进行输入(Input)和输出(Output)操作,此时数据像水流一样从外存储器流向内存。或者从内存流向外存储器,所以系统形象的称文件操作为文件流

    C语言程序对文件的操作采用"文件缓存机制"。就是说在程序对文件的数据读写并不是直接操作文件中的数据,而是系统会为文件在内存中创建"文件缓冲区",程序对文件的操作,其实是在缓冲区进行。

  • 文件的分类

    • 根据数据的存储方式划分
      1. 文本文件(ASCII文件)
      2. 二进制文件
  • 文件的标识

    • 文件系统中:路径+文件名 例如:E:\YQ\code\Homework\20241217
    • C语言程序中:文件指针(文件类型指针),语法:FILE* 指针变量名
  • 文件操作的步骤:

    1. 打开文件
    2. 文件处理(读写文件)
    3. 关闭文件

文件的操作

文件的打开与关闭

打开文件

打开文件,让系统为文件创建文件缓冲区

  • 函数名:fopen()

  • 头文件:#inculde <stdio.h>

  • 函数原型:FILE* fopen(const char *path,const char *mode);

  • 函数功能:打开文件,并为文件创建缓冲区

  • 形参

    • path:目标文件的路径

    • mode:文件打开的方式(r-读、w-写、re-读写)

    type读写性文本/进制文件新建/打开
    r文本打开文件
    w文本新建文件
    a添加文本有就打开无则建新
    r+读写无限制打开
    w+读写无限制建新文件
    a+读写添加无限制有就打开无则建新
  • 返回值

    • 成功:返回文件指针FILE*(缓冲区首地址)
    • 失败:返回NULL
关闭文件

文件关闭,文件使用完毕,一定要释放内存

  • 函数名:fclose()
  • 头文件:#inculde <stdio.h>
  • 函数原型:int fclose(FILE* fp);
  • 函数功能:
    • fp已经打开的文件指针
  • 返回值
    • 成功:返回0
    • 失败:返回EOF(-1)
文件打开与关闭案例
#include <stdio.h>

int main(int argc, char *argv[])
{
    // 在命令行执行./a.out的时候,传递一个需要打开的目录文件的地址
    if (argc < 2)
    {
        printf("输入有误,请按照<%s文件路径>格式输入\n", argv[0]);

        return -1;
    }

    // 根据文件路径打开文件
    FILE *fp = fopen(argv[1], "r");
    // 校验文件是否读取成功
    if (!fp)
    {
        perror("文件打开失败!");
        return -1;
    }

    puts("文件打开成功!\n");

    int ret = fclose(fp);
    if (ret == -1)
        perror("文件关闭失败");
    puts("文件关闭成功");

    return 0;
}

文件的顺序读写

单字符读取
  • 函数名:fgetc
  • 头文件:#inculde <stdio.h>
  • 函数原型:int fgetc(FILE* fp);
  • 函数功能:从fp代表的文件中获取一个字符
  • 形参
    • fp:我们操作的文件指针
  • 返回值
    • 成功:返回读取的字符
    • 失败:或者文件末尾,返回EOF(-1)

方式一(ASCII码)

#include <stdio.h>

int main(int argc, char *argv[])
{
    // 在命令行执行./a.out的时候,传递一个需要打开的目录文件的地址
    if (argc < 2)
    {
        printf("输入有误,请按照<%s文件路径>格式输入\n", argv[0]);

        return -1;
    }

    // 根据文件路径打开文件
    FILE *fp = fopen(argv[1], "r");
    // 校验文件是否读取成功
    if (!fp)
    {
        perror("文件打开失败!");
        return -1;
    }

    char re = 0;
    // 循环读取文件中所有字符
    while ((re = fgetc(fp)) != -1)
    {
        printf("%c", re);
    }

    puts("文件打开成功!\n");

    int ret = fclose(fp);
    if (ret == -1)
        perror("文件关闭失败");
    puts("文件关闭成功");

    return 0;
}

方式二(ASCII值)

#include <stdio.h>

int main(int argc, char *argv[])
{
    // 在命令行执行./a.out的时候,传递一个需要打开的目录文件的地址
    if (argc < 2)
    {
        printf("输入有误,请按照<%s文件路径>格式输入\n", argv[0]);

        return -1;
    }

    // 根据文件路径打开文件
    FILE *fp = fopen(argv[1], "r");
    // 校验文件是否读取成功
    if (!fp)
    {
        perror("文件打开失败!");
        return -1;
    }

    int re = 0;
    // 循环读取文件中所有字符
    while ((re = fgetc(fp)) != EOF)
    {
        printf("%c", re);
    }

    puts("文件打开成功!\n");

    int ret = fclose(fp);
    if (ret == -1)
        perror("文件关闭失败");
    puts("文件关闭成功");

    return 0;
}
多字符读取
  • 函数名:fgets()
  • 头文件:#inculde <stdio.h>
  • 函数原型:char *fgets(char *buf,int size,FILE *fp)
  • 函数功能:从fp代表的文件中获取size个字符(size大小以字节为单位),放置在buf代表的内存中
  • 形参
    • buf:内存空间首地址用于存放读取的字节
    • size:待读取的字符,实际读取size
    • fp:已经打开文件指针
  • 返回值
    • 成功:返回buf
    • 失败:或者文件末尾,返回NULL

案例:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    // 在命令行执行./a.out的时候,传递一个需要打开的目录文件的地址
    if (argc < 2)
    {
        printf("输入有误,请按照<%s文件路径>格式输入\n", argv[0]);

        return -1;
    }

    // 根据文件路径打开文件
    FILE *fp = fopen(argv[1], "r");
    // 校验文件是否读取成功
    if (!fp)
    {
        perror("文件打开失败!");
        return -1;
    }

    char ch[64] = {0};
    // 循环读取文件中所有字符
    while (fgets(ch, 64, fp) != NULL)
    {
        printf("%s", ch);
        memset(ch, 0, sizeof(ch));
    }

    puts("文件打开成功!\n");

    int ret = fclose(fp);
    if (ret == -1)
        perror("文件关闭失败");
    puts("文件关闭成功");

    return 0;
}
单字符写入
  • 函数名:fputc();
  • 头文件:#inculde <stdio.h>
  • 函数原型: int fputc(int c,FILE* fp);
  • 函数功能:想fp代表的文件中写入一个字符
  • 形参
    • int c:待写入的字符
    • fp:已打开文件指针
  • 返回值
    • 成功:返回字符c
    • 失败:返回EOF(-1)
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    // 在命令行执行./a.out的时候,传递一个需要打开的目录文件的地址
    if (argc < 3)
    {
        printf("输入有误,请按照<%s 文件路径 文本数据>格式输入\n", argv[0]); // ./a.out./ demo01.c hello return -1;
    }
    // 根据提供的文件路径,打开文件(mode:r,w,rw)
    FILE *fp = fopen(argv[1], "w");
    // 校验文件是否读取成功
    if (!fp)
    {
        perror("文件打开失败!\n");
        return -1;
    }
    puts("文件打开成功!");

    // 单字符写入
    // 借助循环,一个字符一个字符写入
    while (*argv[2] != '\0')
    {
        fputc(*argv[2], fp); //
        argv[2]++;           // 指针偏移
    }

    // 关闭打开的文件
    int ret = fclose(fp);
    if (ret == -1)
    {
        perror("文件关闭失败!");
        return -1;
    }
    puts("文件关闭成功!");
    return 0;
}
多字符写入
  • 函数名:fputs();
  • 头文件:#inculde <stdio.h>
  • 函数原型: int fputs(const char* buf,FILE* fp);
  • 函数功能:想fp代表的文件中写入多个字符
  • 形参
    • buf:待写入的字符数组
    • fp:已打开文件指针
  • 返回值
    • 成功:返回非负整数>0
    • 失败:返回EOF(-1)
/**
 * 多字符写入
 */
#include <stdio.h>
int main(int argc, char **argv)
{
    if (argc < 3)
    {
        printf("输入有误,请按照<%s 文件路径 文本数据>格式输入\n", argv[0]);
        return -1;
    }
    FILE *fp = fopen(argv[1], "w");
    if (!fp)
    {
        perror("文件打开失败!");
        return -1;
    }
    // 单字符写入
    // ./a.out file1.txt I_Love_Your
    fputs(argv[2], fp);
    fclose(fp);
    return 0;
}
文件拷贝
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    // 在命令行执行./a.out的时候,传递一个需要打开的目录文件的地址
    if (argc < 2)
    {
        printf("输入有误,请按照<%s 被读文件路径 被写文件路径>格式输入\n", argv[0]); // ./a.out./ demo01.c return -1;
    }
    // 根据提供的文件路径,打开文件(mode:r,w,rw)
    FILE *fp_r = fopen(argv[1], "r");
    FILE *fp_w = fopen(argv[2], "w");
    // 校验文件是否读取成功
    if (!fp_r || !fp_w)
    {
        perror("文件打开失败!\n");
        return -1;
    }
    puts("文件打开成功!");
    // 创建一个缓冲区(也就是每次读取字节的大小)
    char buf[64] = {0};
    // 循环读取文件中所有字符
    while (fgets(buf, 64, fp_r) != NULL)
    {
        // 写入文件
        fputs(buf, fp_w);
        // 每读取一次,都需要清空缓冲区
        memset(buf, 0, sizeof(buf));
    }
    // 关闭打开的文件
    int ret1 = fclose(fp_r);
    int ret2 = fclose(fp_w);
    if (ret1 == -1 || ret2 == -1)
    {
        perror("文件关闭失败!");
        return -1;
    }
    puts("文件关闭成功!");
    return 0;
}
判别文件结束
  • 函数名:feof(fp)
  • 头文件:#inculde <stdio.h>
  • 函数原型:int feof(FILE* fp);
  • 函数功能:在读fp指向的文件时判断是否遇到文件结束
  • 形参
    • fp:已打开的文件指针
  • 返回值
    • 文件未读取完毕:返回0
    • 文件已读取完毕:返回非0

数据块的读写

数据块的读取(二进制)
  • 函数名:fread

  • 函数原型:size_t fread(void *ptr,size_t size,size_t count,FILE *fp);

  • 函数功能:从fp指向的文件中以字节为单位(一个数据块)读取count个数据块存放在内存中

  • 形参

    • ptr:内存空间首地址,用于存放读取的数据
    • size:数据块大小,以字节为单位
    • count:待读取的数据块的个数
    • fp:已打开的文件指针
  • 返回值

    • 成功:返回实际读取的字节数大小
    • 失败:读取完毕返回<0
    #include <stdio.h>
    #define SIZE 2
    
    struct Student
    {
        char name[20];
        int num;
        int age;
        char address[50];
    } stus[SIZE];
    
    /*
        保存学生信息到文件
    */
    
    int save()
    {
        FILE *fp;
        int i;
        if ((fp = fopen("stu", "wb")) == NULL) // stu保存文件的名称
        {
            perror("文件打开失败!");
            return -1;
        }
    
        // 写入数据
        for (i = 0; i < SIZE; i++)
        {
            fwrite(&stus[i], sizeof(struct Student), 1, fp);
        }
    
        // 关闭文件
        fclose(fp);
    
        return 0;
    }
    
    int main()
    {
        int i;
    
        printf("请输入学生的信息:姓名、学号、年龄、住址\n");
        for (i = 0; i < SIZE; i++)
        {
            scanf("%s%d%d%s", stus[i].name, &stus[i].num, &stus[i].age, stus[i].address);
            // 保存文件
        }
        save();
    
        return 0;
    }
    
数据块的写入
  • 函数名:fwrite
  • 函数原型:size_t fwrite(const void *ptr,size_t size,size_t count,FILE *fp);
  • 函数功能:向fp指向的文件中以字节为单位(一个数据块)写入count个数据块到fp
  • 形参
    • ptr:内存空间首地址,用于存放写入的数据
    • size:数据块大小,以字节为单位
    • count:待写入的数据块的个数
    • fp:已打开的文件指针
  • 返回值
    • 成功:返回实际写入的字节数大小
    • 失败:写入完毕返回<0
#include <stdio.h>
#define SIZE 2 // 存放学生的个数
// 创建学生结构体
struct Student
{
    char name[20];
    int num;
    int age;
    char addr[50];
} stus[SIZE];
int main(int argc, char *argv[])
{
    int i;
    FILE *fp;
    if ((fp = fopen("stu", "rb")) == NULL) // rb 以二进制读取
    {
        perror("文件打开失败!");
        return -1;
    }
    // 循环读取二进制读取
    for (i = 0; i < SIZE; i++)
    {
        fread(&stus[i], sizeof(struct Student), 1, fp);
        // 将读取的数据输出到控制台
        printf("%-10s%-4d%-4d%-20s\n", stus[i].name, stus[i].num, stus[i].age, stus[i].addr);
    }
    // 关闭文件
    fclose(fp);
    return 0;
}
文件的随机读写

说明:C语言允许在读写文件内容时,可在指定位置上读写数据

文件随机读写的核心操作:文件位置指针的定位

文件位置指针移动方法:

  1. rewind

    • 头文件:#include <stdio.h>
    • 函数原型:void rewind(FILE *fp);
    • 函数功能:将文件位置指针定位到文件开头
    • 形参:
      • fp:已经打开文件的指针
    • 返回值:空类型
    #include <stdio.h>
    
    /*
        有一个磁盘文件第一次将他内容通过控制台输出
        第二次将其复制到另一个文件
    */
    
    int main()
    {
        // 创建两个指针,用来接收打开的文件
        FILE *fp1, *fp2;
    
        // 校验
        if (!fp1 && !fp2)
        {
            perror("文件打开失败!");
            return -1;
        }
    
        puts("文件打开成功!\n");
    
        // 打开文件
        fp1 = fopen("1.txt", "r");
        fp2 = fopen("2.txt", "w");
    
        // 第一次,读取文件内容通过控制台打印
        while (!feof(fp1)) // feof函数检测文件是否读完
        {
            putchar(getc(fp1));
        }
    
        rewind(fp1);
    
        // 第二次,读取文件内容将其拷贝至2.txt
        while (!feof(fp1))
        {
            putc(getc(fp1), fp2);
        }
    
        fclose(fp1);
        fclose(fp2);
    
        return 0;
    }
    
  2. fseek

    • 头文件:#include <stdio.h>

    • 函数原型: int fseek(File *fp,long offset,int whence);

    • 函数功能:将文件位置指针定位到指定位置

    • 形参

      • fp:已打开的文件的指针

      • offset:相对参考位置的偏移量

      • whence:参考位置

        • SEEK_SET或0:表示文件头

        • SEEK_CUR或1:表示当前读写位置

        • SEEK_END或2:表示文件尾

    • 返回值:

      • 成功:0

      • 失败:-1

    #include <stdio.h>
    #include <stdlib.h>
    
    /*
        在磁盘文件上存储10个学生的数据
        要求1,3,5,7,9这五个学生信息输入计算机,并显示
    */
    
    // 定义学生结构体
    struct Student
    {
        char name[20];
        int id;
        char sex;
    };
    
    typedef struct Student Stu;
    
    Stu stus[3] = {0};
    
    int main()
    {
        FILE *fp = NULL;
        int len = sizeof(stus) / sizeof(stus[0]);
    
        printf("%d", len);
    
        // 写入数据
        if ((fp = fopen("stu", "wb")) == NULL) // stu保存文件的名称
        {
            perror("文件打开失败!");
            return -1;
        }
    
        printf("请输入学生的信息:姓名、学号、性别\n");
        for (int i = 0; i < len; i++)
        {
            scanf("%s%d %c", stus[i].name, &stus[i].id, &stus[i].sex);
        }
    
        fwrite(stus, sizeof(Stu), len, fp);
        fclose(fp); // 关闭文件
    
        // 打开文件
        if ((fp = fopen("stu", "rb")) == NULL)
        {
            perror("文件打开失败!");
            return -1;
        }
    
        for (int i = 0; i < len; i += 2)
        {
            // 跳过文件位置,改变文件指针的指向
            fseek(fp, i * sizeof(Stu), 0);
            fread(&stus[i], sizeof(Stu), 1, fp);
            printf("%s %d %c\n", stus[i].name, stus[i].id, stus[i].sex);
        }
    
        fclose(fp);
    
        return 0;
    }
    
  3. ftell

    • 头文件#include <stdio.h>
    • 函数原型:long ftell(FILE* fp);
    • 函数功能:获取文件指针当前位置
    • 形参
      • fp:已打开文件的指针
    • 返回值
      • 成功:文件位置指针的当前位置
      • 失败:-1
#include <stdio.h>
int main(int argc, char *argv[])
{
    long p;
    FILE *fp;
    if ((fp = fopen(argv[1], "a")) == NULL) // 此时的mode:a代表追加(a是append)
    {
        perror("文件打开失败!");
        return -1;
    }
    // 获取当前位置
    p = ftell(fp);
    printf("p=%ld\n", p);
    // 向文件添加数据
    fputs("data", fp);
    p = ftell(fp);
    printf("p=%ld\n", p);
    fclose(fp);
    return 0;
}

http://www.kler.cn/a/460064.html

相关文章:

  • 数据挖掘——聚类
  • 【C++】B2089 数组逆序重存放
  • c++表达范围勿用数学符号
  • typescript中的interface理解
  • 【数据结构】(Python)差分数组。差分数组与树状数组结合
  • 小程序基础 —— 02 微信小程序账号注册
  • 洛谷 P1614 爱与愁的心痛 C(滑动窗口)
  • Django serializers:把ValidationError处理的更优雅
  • 计算机网络与通信复习
  • Dockerfile 实战指南:解锁高效容器化开发
  • Android 旋转盘导航栏
  • 【UE5 C++课程系列笔记】15——Assert的基本使用
  • vue3<script setup>中使用Swiper
  • 第八节:GLM-4v-9b模型的大语言模型源码解读(ChatGLMForConditionalGeneration)
  • windows C#-带有命名方法的委托与匿名方法
  • 基于springboot的校园新闻网站系统
  • [创业之路-225]:《华为闭环战略管理》-4-华为的商业智慧:在价值链中探索取舍之道与企业边界
  • WAP短信格式解析及在Linux下用C语言实现
  • 【Spring MVC 核心机制】核心组件和工作流程解析
  • 【OTA】论文学习笔记--《基于RTOS的车载ECU双分区OTA升级技术分析报告》
  • 3.阿里云flinkselectdb-py作业
  • 什么是微服务、微服务如何实现Eureka,网关是什么,nacos是什么
  • PyTorch快速入门教程【小土堆】之Sequential使用和小实战
  • 【RK3588 Linux 5.x 内核编程】-内核IO复用与select
  • 防火墙基础-工作原理
  • 爱思唯尔word模板