第17天文件操作项目练习

news/2024/11/5 5:59:28 标签: c语言, 学习, 链表

文件操作

函数名:rewind()

函数原型:void rewind(FILE *stream);

函数功能:光标移动到文件开头

函数参数:FILE *stream:fp

函数返回值:无

函数使用: rewind(fp) == fseek(fp,0,0);

函数名:ftell()

函数原型:long ftell(FILE *stream);

函数功能:计算从文件开头到当前光标位置的字节数

函数参数:FILE *stream:fp

函数返回值:返回文件开头到当前光标位置的字节数

函数使用: 可用于计算文件大小操作: fseek(fp,0,2); long n = ftell(fp);

函数名:remove()

函数原型:int remove(const char *pathname);

函数功能:删除文件

函数参数:const char *pathname:文件路径---字符串形式

函数返回值:成功返回 0,失败返回-1

函数使用: remove(“../1.txt”);

函数名:feof()

函数原型:int feof(FILE *stream);

函数功能:判断光标是否在文件末尾

函数参数:FILE *stream:fp

函数返回值:到文件末尾,返回非 0,没有到文件末尾,返回 0

函数使用:

rewind(fp); 
while(feof(fp)==0) {
    Node *new = Create_Node(); 
    fscanf(“%s %d\n”,new->inf.name,&new->inf.id);
     //头插法或尾插法加入到链表中 
}

函数名:fgets()

函数原型:char *fgets(char *s, int size, FILE *stream);

函数功能:从文件流读取指定长度的字符串

函数参数:

  • char *s:数据在程序中保存的位置
  • int size:字符串总长度,要比想要读取的有效字符个数多 1 想要读 5 个有效字符,需要填 6
  • FILE *stream:fp

函数返回值:返回读取到的字符串的所在地址

函数使用:

 char a[100]={0}; 
 fgets(a,100,fp);
//实际读取 99 个有效字符,还有一个 ‘\0’

函数名:fputs()

函数原型:int fputs(const char *s, FILE *stream);

函数功能:向指定文件写入字符串

函数参数:

  • const char *s:想要写入的字符串
  • FILE *stream:fp

函数返回值:成功返回非负数,失败返回 EOF

函数使用: fputs(“hello”,fp);

函数名:fgetc()

函数原型:int fgetc(FILE *stream);

函数功能:从指定文件读取一个字符

函数参数:FILE *stream:fp

函数返回值:返回读取到的字符

函数使用: char a=fgetc(fp);

函数名:fputc()

函数原型:int fputc(int c, FILE *stream);

函数功能:写入一个字符到指定文件

函数参数:int c:想要写入的字符 FILE *stream:fp

函数返回值:成功返回写入的字符,失败返回 EOF

函数使用: fputc(‘a’,fp);

函数名:fwrite()

函数原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

函数功能:ptr 起始的数据块,一块 size 大小,共写入 nmemb 个,到文件 stream

函数参数:

  • const void *ptr:需要写入数据块的起始地址
  • size_t size:一个数据块的大小
  • size_t nmemb:一共几个数据块
  • FILE *stream:fp

函数返回值:返回写入的数据块的个数

函数使用: 块读块写---数据带数据类型
 

int a[5]; //1*20 20*1 5*4
fwrite(a,4,5,fp);

函数名:fread()

函数原型:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

函数功能:fread 函数从文件流 stream 中读取 nmemb 个元素,每个元素的大小为 size 字节,并将这些数据存储到由 ptr 指向的内存区域中。

函数参数:

  • void *ptr:读取的数据块保存位置的起始地址
  • size_t size:一个数据块的大小
  • size_t nmemb:一共几个数据块
  • FILE *stream:fp

函数返回值:返回读取的数据块的个数
函数使用:

 int a[5]={0}; 
 fread(a,20,1,fp);
  • fread 函数从文件流 fp 中读取数据,并将其存储到数组 a 中。
  • 参数解释:
    • a:指向存储读取数据的内存区域的指针。
    • 20:每个元素的大小(以字节为单位)。在这里,20 表示每次读取 20 个字节。
    • 1:要读取的元素个数。在这里,1 表示读取 1 个 20 字节的块。
    • fp:指向文件流的指针。

示例代码

#include <stdio.h>
int main()
{
    // 尝试以读写方式打开名为"2.txt"的文件,如果文件不存在则创建
    FILE * fp = fopen("2.txt","w+");  
    int a[5]={1,2,3,4,5};
    // 以格式化输出的方式将数组 a 的元素写入文件(此部分被注释)
    // fprintf(fp,"%d%d%d%d%d",a[0],a[1],a[2],a[3],a[4]);  
    // 以二进制形式将数组 a 的内容写入文件,每个元素大小为 4 字节,共写入 5 个元素
    fwrite(a,4,5,fp);  

    int b[5]={0};
    // 将文件指针重新定位到文件开头
    rewind(fp);  
    // 从文件中读取数据到数组 b 中,每次读取 20 字节,读取 1 次
    fread(b,20,1,fp);  
    // 打印数组 b 的元素
    printf("%d%d%d%d%d\n",b[0],b[1],b[2],b[3],b[4]);  

    // 关闭文件
    fclose(fp);  

    return 0;
}

这段代码主要实现了向文件写入数据和从文件读取数据的功能。首先,它打开一个文件并将整型数组 a 的内容以二进制形式写入该文件。 然后,重新定位文件指针到开头,从文件读取数据到另一个整型数组 b ,最后打印数组 b 的元素并关闭文件。  

项目练习

这里继续以学生信息管理系统为练习。

我这里实现了新增信息、展示信息、修改信息、查询信息、删除信息、插入信息、排序,共7个功能。将其做模块化应用。

头文件stu.h

#ifndef _STU_H_
#define _STU_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct info{
    int id;
    char name[20];
    float score;
};
typedef struct student{
    struct info mesg;
    struct student *next;
}M,*list;

extern list head;

char menu(void);
list Create(void);
void Add(void);
void Load(void);
void Save(list head);
void Print(void);
list Search(void);
void Change(void);
void Delete(void);
void Insert(void);

#endif

我的头文件里包含了所有函数的声明。同时,将信息封装为结构体,嵌套到链表中。并且,将头指针外部声明。

主函数main.c

#include "stu.h"

list head=NULL;
int main() {
    Load();//调用数据加载
    char op = 0;
    while (1) {
        op = menu();
        switch (op) {
            case '0':  // 0. 退出
                return 0;
            case '1':  // 1. 新增信息
                Add();
                break;
         case '2':  // 2. 展示信息
                Print();
                break;
            case '3':     
                Search();//查询信息
                break;
       case '4':  // 4. 修改信息
                Change();
                break;
       case '5':  // 5. 删除信息
                Delete();
                break;
          case '6':  // 6. 插入信息
                Insert();
                break;
                case '7':  // 7. 排序
                Sequence();
                break;
           default:
                printf("Error!\n");
        }
        printf("Enter to continue...\n");
        while(getchar()!='\n');//清空缓冲区
        system("clear");//清屏
    }
    return 0;
}
char menu(void) {//菜单函数
    char val = 0;  // 定义选择项
    printf("What do you want to do?\n");
    printf("0. 退出\t1. 新增信息\t2. 展示信息\t3. 查询信息\n");
    printf("4. 修改信息\t5. 删除信息\n");
    printf("6. 插入信息\t7. 排序\n");
    scanf("%c", &val);
    while (getchar() != '\n');
    return val;
}

这里我做了switch菜单用于管理系统功能。并将菜单选项封装为函数。

在主函数里先调用文件数据加载函数,并在选择功能后做了清空缓存区的处理。

Add.c

#include "stu.h"

void Add(void){
    list newN=Create();
    if(newN==NULL){
        printf("error\n");
        return ;
    }
    int id;
    while (1) {
        printf("请输入id信息\n");
        scanf("%d", &id);
        while (getchar() != '\n');

        // 检查 id 是否已存在
        list cur = head;
        int exists = 0;
        while (cur != NULL) {
            if (cur->mesg.id == id) {
                exists = 1;
                break;
            }
            cur = cur->next;
        }

        if (!exists) {
            break;  // 如果 id 唯一,跳出循环
        } else {
            printf("该 id 已存在,请输入新的 id。\n");
        }
    }
    newN->mesg.id = id;
    
    printf("请输入name信息\n");
    scanf("%s",newN->mesg.name);
    while(getchar()!='\n');
    printf("请输入score信息\n");
    scanf("%f",&newN->mesg.score);
    while(getchar()!='\n');
    newN->next=head;
    head=newN;
    Save(head);//调用保存函数写入文件
}



void Save(list head){
     FILE *fp = fopen("message.txt", "w"); //以写形式打开文件
     if (fp == NULL) {  // 检查文件是否成功打开
           perror("fopen");  // 如果打开失败,输出错误信息
           return ;  // 返回
    }
    list cur=head;
    while(cur!=NULL){//cur遍历链表,将全部信息重新写入覆盖数据
fprintf(fp,"%d %s %f\n",cur->mesg.id,cur->mesg.name,cur->mesg.score);
        cur=cur->next;
    }
    fclose(fp);
}


void Load(void){
    FILE *fp = fopen("message.txt", "r"); //以读形式打开文件
    if(fp==NULL){
        return;
    }
    
     list newN;
    while (feof(fp)==0) {
        newN = Create();
        if (newN == NULL) {
            perror("malloc");
            fclose(fp);
            return;
        }
if (fscanf(fp, "%d %s %f\n", &newN->mesg.id, newN->mesg.name, &newN->mesg.score) == EOF) //??
{
            free(newN);  // 如果读取失败,释放新分配的内存
            break;
        }
        newN->next = head;
        head = newN;
    }
    fclose(fp);
}

在这个源文件里,我定义了三个函数:新增数据、数据写入、数据加载

在新增数据函数Add()中,

这个函数用于向链表中添加新的节点。

首先创建一个新的链表节点 newN ,如果内存分配失败(newN 为 NULL ),打印错误信息并返回。

然后进入一个无限循环,提示用户输入 id 信息,并使用一个内层循环检查输入的 id 是否已经在链表中存在。如果不存在,就跳出无限循环;如果存在,提示用户重新输入。

接着,将用户输入的合法 id 赋值给新节点的 id 成员。

之后,提示用户输入 name 和 score 信息,并将其分别赋值给新节点的相应成员。

然后,将新节点插入到链表头部(通过让新节点的 next 指针指向原来的头节点,然后更新头节点为新节点)。

最后,调用 Save 函数将更新后的链表数据写入文件。

在数据写入函数Save()中,

这个函数的作用是将一个链表中的数据保存到一个名为 message.txt 的文件中。首先,函数以只写模式打开文件,如果文件打开失败(fp 为 NULL),会输出错误信息并返回。

然后,定义一个链表节点指针 cur 并初始化为链表的头节点 head 。

通过一个 while 循环,只要 cur 不为 NULL (即未到达链表末尾),就使用 fprintf 函数将当前节点的数据(包括 id 、name 和 score )按照指定的格式写入文件中,格式为 %d %s %f\n ,分别对应整数、字符串和浮点数,并在每个数据项后添加换行符。

每次写入后,将 cur 指针移动到下一个节点。

最后,关闭文件,完成数据的保存操作。

在数据加载函数Load()中,

主要功能是从一个名为 message.txt 的文件中读取数据,并将其构建为一个链表

函数首先以只读模式打开文件,如果文件打开失败(fp 为 NULL),则直接返回。

然后创建一个新的链表节点 newN 。

在 while 循环中,只要还未到达文件末尾(通过 feof(fp)==0 判断),就继续执行以下操作:

尝试为新节点分配内存,如果内存分配失败(newN 为 NULL),打印错误信息并关闭文件后返回。

使用 fscanf 函数从文件中读取数据到新节点的成员中。如果读取失败(返回值为 EOF),释放新分配的内存并退出循环。

如果读取成功,将新节点插入到链表头部(通过让新节点的 next 指针指向原来的头节点,然后更新头节点为新节点)。

最后,关闭文件。

其中的 if (fscanf(fp, "%d %s %f\n", &newN->mesg.id, newN->mesg.name, &newN->mesg.score) == EOF) 这部分,它的作用是检查从文件中读取数据的操作是否成功。如果读取不成功(即达到了文件末尾或读取过程中出现错误),返回值为 EOF,就会执行后面的释放内存和退出循环的操作。

 create.c

#include "stu.h"

list Create(void){
    list newN=(list)malloc(sizeof(M));
    if(newN==NULL){
        perror("malloc");
        return NULL;
    }
    memset(newN,0,sizeof(M));
    return newN;
}

这个函数用于创建一个新的链表节点。

首先,使用 malloc 函数分配一个大小为 sizeof(M) 的内存空间,并将返回的指针强制转换为 list 类型,然后赋值给 newN 。如果内存分配失败(newN 为 NULL ),打印错误信息并返回 NULL 。

接着,使用 memset 函数将新分配的内存空间初始化为 0 。

最后,返回新创建的节点指针。

Print.c

#include "stu.h"
void Print(void){
    if (head == NULL) {
        printf("No students in the list.\n");
        return;
    }
    list i=head;
    printf("%-8s%-8s%-8s\n","id","name","score");		
    while(i!=NULL){
    	printf("%-4d\t%-8s%-8.2f\n",i->mesg.id,i->mesg.name,i->mesg.score);
        i=i->next;
    }
    printf("\n");
}

这个函数用于遍历打印输出链表中的数据

Search.c

#include "stu.h"
list Search(void){
    list cur=head;
    int flag=0,tmp=0;
    printf("请输入需处理的学生id:");
    scanf("%d",&tmp);
    while(getchar()!='\n');
    while(cur!=NULL){
        if(tmp==cur->mesg.id){
printf("%-8s%-8s%-8s\n","id","name","score");
printf("%-4d\t%-8s%-8.2f\n",cur->mesg.id,cur->mesg.name,cur->mesg.score);
            flag++;
            return cur;
        }
        cur=cur->next;
    }
    if(flag==0){
        printf("这儿没有这个学生\n");
        return NULL;
    }
}

这函数用来通过学号id查询学生信息,并返回查询到的信息节点指针

Change.c

#include "stu.h"

void Change(void){
    list cur=Search();
    if (cur == NULL) {
        printf("未找到该学生信息,无法修改。\n");
        return;
    }
    printf("修改成绩为:");
    scanf("%f",&cur->mesg.score);
     Save(head);//调用保存函数写入文件
}

这个函数用来更改学生成绩数据,最后更新到文件中。

 Delete.c

#include "stu.h"

void Delete(void){
    list cur=Search();
    if (cur == NULL) {
        printf("未找到该学生信息,无法修改。\n");
        return;
    }
    if(cur==head){
        head=cur->next;
    }else{
        list i=head;
        while(i->next==cur){
            i->next=cur->next;
        }
        free(cur);
     Save(head);//调用保存函数写入文件
}
}

这个函数用来删除学生信息

Insert.c

#include "stu.h"

void Insert(void){
    int sec=0;
    printf("要插入在0.第1个么?1.或者谁之后?\n");
    scanf("%d",&sec);
    if(sec==0){
        Add();
        return;
    }else{
        
        list cur=Search();
        list ins=Create();
        int id;
        while (1) {
            printf("请输入id信息\n");
            scanf("%d", &id);
            while (getchar() != '\n');

        // 检查 id 是否已存在
        list cur1 = head;
        int exists = 0;
        while (cur1 != NULL) {
            if (cur1->mesg.id == id) {
                exists = 1;
                break;
            }
            cur1 = cur1->next;
        }

        if (!exists) {
            break;  // 如果 id 唯一,跳出循环
        } else {
            printf("该 id 已存在,请输入新的 id。\n");
        }
    }
    ins->mesg.id = id;
    
    printf("请输入name信息\n");
    scanf("%s",ins->mesg.name);
    while(getchar()!='\n');
    printf("请输入score信息\n");
    scanf("%f",&ins->mesg.score);
    while(getchar()!='\n');
        
        ins->next=cur->next;
        cur->next=ins;
     Save(head);//调用保存函数写入文件
}
}

这个函数用于插入信息。首先判断插入位置,再分情况处理。

Sequence.c

#include "stu.h"

void Sequence(void){
    int op=0;
    printf("请选择排序规则:0.按学号排序\t1.按成绩排序\n");
    scanf("%d",&op);
    if(op){//按成绩
        list cur=head;
        list later=head;
        for(cur=head;cur->next!=NULL;cur=cur->next)
        {
            for(later=head;later->next!=NULL;later=later->next){
                if(later->mesg.score<later->next->mesg.score){
                    struct info tmp =later->next->mesg;
                    later->next->mesg=later->mesg;
                    later->mesg=tmp;
                }
            }
        }
    }else{//按学号
        list cur=head;
        list later=head;
        for(cur=head;cur->next!=NULL;cur=cur->next)
        {
            for(later=head;later->next!=NULL;later=later->next){
                if(later->mesg.id>later->next->mesg.id){
                    struct info tmp =later->next->mesg;
                    later->next->mesg=later->mesg;
                    later->mesg=tmp;
                }
            }
        }
    
    }
    Save(head);
}

最后排序,整理信息。


http://www.niftyadmin.cn/n/5739041.html

相关文章

【系统架构设计师】2022年真题论文: 论湖仓—体架构及其应用(包括解题思路和素材)

更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 真题题目(2022年 试题4)解题思路湖仓一体架构概念与特点湖仓一体架构技术组成湖仓一体架构的应用场景论文素材参考真题题目(2022年 试题4) 随着5G、大数据、人工智能、物联网等技术的不断成熟,各行各业的业务…

针对告警数量、告警位置、告警类型等参数进行统计,并做可视化处理的智慧能源开源了。

一、简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;在强大视频算…

Leetcode328奇偶链表,Leetcode21合并两个有序链表,Leetcode206反转链表 三者综合题

题目描述 思路分析 这题的思路就和我们的标题所述一样&#xff0c;可以看作是这3个题的合并&#xff0c;但是稍微还有一点点区别 比如&#xff1a;奇偶链表这道题主要是偶数链在了奇数后面&#xff0c;字节这个的话是奇偶链表分离了 所以字节这题的大概思路就是&#xff1a; …

【测试工具】Fastbot 客户端稳定性测试

背景 做这个主要为了发版之前提前发现崩溃&#xff0c;风险前置。适合客户端很重的业务。 优点&#xff1a;你不改动也能用&#xff0c; 维护成本不高。 缺点&#xff1a;容易进入H5页面无法返回&#xff0c;效果有限。 备注&#xff1a;我这边接手别人维护&#xff0c;公司…

第一章 微服务入门

目录 一、引言 二、传统单体架构的缺点 三、微服务的概念 ‌四、微服务与SOA的区别 4.1. 微服务架构 4.2. SOA架构 4.3. 区别对比 五、微服务特性 ‌5.1. 微服务的核心特性‌ 5.2. 微服务的优点 5.3. 微服务的缺点 一、引言 在了解微服务之前&#xff0c;我们先…

RDD转换算子:【filter】

功能&#xff1a; 过滤数据 对RDD集合中的每个元素调用一次参数中的表达式对数据进行过滤&#xff0c;符合条件就保留&#xff0c;不符合就去除 语法&#xff1a; def filter(self, f: T -> bool ) -> RDD[T]f&#xff1a;代表参数是一个函数 T&#xff1a;代表RDD中的…

【JAVA】Java基础—Java开发环境搭建:安装JDK与IDE(如IntelliJ IDEA、Eclipse)

Java是一种强大的编程语言&#xff0c;广泛应用于各种领域&#xff0c;包括企业级应用、移动应用&#xff08;如Android&#xff09;、Web应用和大数据处理等。Java的“编写一次&#xff0c;到处运行”&#xff08;Write Once, Run Anywhere, WORA&#xff09;特性使得它在跨平…

HTMLCSS:呈现的3D树之美

效果演示 这段代码通过HTML和CSS创建了一个具有3D效果的树的图形&#xff0c;包括分支、树干和阴影&#xff0c;通过自定义属性和复杂的变换实现了较为逼真的立体效果。 HTML <div class"container"><div class"tree"><div class"…