首页/新闻资讯/正文详情

C语言格式化输入输出与通讯录持久化

发布时间:2026/9/24 5:01:49 来源:云帆数科 栏目:资讯中心
C语言格式化输入输出与通讯录持久化
C 语言课堂练习几组输入输出函数以及通讯录怎么存档这次课的作业一部分是比较scanf、fscanf、sscanf这些函数另一部分是给之前写的动态通讯录加上文件保存。我刚看题时觉得它们的名字太像了背函数名很容易背混。后来换了个办法先看数据从哪儿读、最后写到哪儿区别就清楚多了。一、先把几组函数分清楚操作标准输入或输出指定的文件流内存中的字符串格式化读取scanf从stdin读fscanf从传入的FILE *读sscanf从字符串读格式化写入printf写到stdoutfprintf写到传入的FILE *sprintf写到字符数组我以前看到FILE *第一反应就是磁盘文件。其实stdin、stdout也是流也能传给对应的函数。fscanf从流里读fprintf往流里写到了sscanf和sprintf处理对象就换成了内存里的字符串。C 标准草案的格式化输入输出章节和 Microsoft 的sscanf说明也把字符串与流分开说明。拿Alice 20举个例子。读入整数时要传地址读完还要检查返回值不能只看变量里有没有数字。intage;charname[20];if(scanf(%d,age)1)printf(age%d\n,age);FILE*fpfopen(people.txt,w);if(fp!NULL){fprintf(fp,%s %d\n,Alice,20);fclose(fp);}if(sscanf(Alice 20,%19s %d,name,age)2)printf(%s: %d\n,name,age);这里写%19s是因为name只有 20 个位置还得给末尾的\0留一个。如果要把格式化结果写进数组我更倾向于用snprintf(buf, sizeof buf, ...)sprintf不接收数组容量内容一长就可能写过界。Microsoft 的sprintf说明也建议考虑snprintf。题目 1572 我选D。前面三项分别说标准流、指定流和字符串读取D 说sprintf把数据写到“输出流”这里写错了目标应该是字符数组。二、feof不能拿来预测下一次读取feof(fp)这个名字很容易让我理解成“文件现在是不是读完了”。准确地说它查的是 EOF 标志。文件里的最后一个字节已经读走了标志也可能还是 0再读一次确实读不到标志才会被设置。Microsoft 的feof文档有一个读完 10 字节、再尝试读第 11 字节的例子。这样再看while (!feof(fp))问题就出来了一次读取已经失败循环体却可能还在用上一次的数据。我现在会让读取函数的返回值决定要不要继续处理intch;// fgetc 要用 int 接收才能表示 EOF。while((chfgetc(fp))!EOF){putchar(ch);}if(ferror(fp)){/* 这是读取错误不是正常读到末尾。 */}/* 按行读取while (fgets(line, sizeof line, fp) ! NULL) { ... } *//* 按记录读取while (fread(person, sizeof person, 1, fp) 1) { ... } */还有一个容易漏掉的地方fgetc返回EOF、fgets返回NULL或者fread没读够都不一定是正常到文件末尾也可能是读取出错。这时再用feof和ferror分原因。fgetc返回值、fgets返回值、fread返回值的官方说明都提到了这两种情况。题目 1573 如果按课堂上强调的用法选单项我会选A。A 说“用feof判断文件是否读取结束”容易被理解成读之前先问它还有没有数据。但这题的措辞确实有点含糊若把 A 理解成“读取失败以后检查 EOF 标志”它又说得通。C、D 给出的返回值也只能说明这一次没有按预期读到数据原因还得再查。我给自己记的版本是先看读取函数的返回值再用feof、ferror判断为什么停下。三、给原来的通讯录加上保存和加载原来的通讯录里PeoInfo记录姓名、年龄、性别、电话和地址Contact管理动态数组、当前人数与容量。代码在 GitHub 的 contact 目录。联系人如果只放在内存里退出后一释放就留不住这次要让下次启动还能看到他们。程序启动时InitContact初始化数组接着由LoadContact用rb模式打开contact.dat。第一次运行没有这个文件就从空通讯录开始。读取时一次fread一个PeoInfo只有返回 1才扩容并放进数组循环结束后还要看ferror。选退出时则反过来SaveContact用wb打开文件fwrite写入当前sz条记录比较实际写入的个数再关闭文件。最后才由DestroyContact释放动态数组。翻源码时我还发现一个没处理好的地方SaveContact返回void菜单里调用完就打印“通讯录已经保存”。要是打开文件或写入失败这句话照样会出现。而且wb打开已有文件会截断旧内容所以“打开成功”和“数据保存成功”不能画等号。后面如果继续改这个项目我会让保存函数把成功与否返回给主程序。四、我用来练习的单文件版本原项目还有查找、修改、删除等功能。为了单独练习文件读写我照着它的数据结构写了下面这个小版本只留添加、显示、保存和加载。示例文件名是contact_demo.dat不会覆盖原项目的contact.dat。输入时各字段用空格隔开所以这里还不能录入带空格的姓名或地址。#includeerrno.h#includestdio.h#includestdlib.h#defineDATA_FILEcontact_demo.dat#defineINITIAL_CAPACITY3#defineGROW_STEP2typedefstructPeoInfo{charname[20];intage;charsex[5];chartele[12];charaddr[30];}PeoInfo;typedefstructContact{PeoInfo*data;size_tsize;size_tcapacity;}Contact;staticintEnsureCapacity(Contact*book){if(book-sizebook-capacity)return1;size_tnextbook-capacity0?INITIAL_CAPACITY:book-capacityGROW_STEP;PeoInfo*p(PeoInfo*)realloc(book-data,next*sizeof(PeoInfo));if(pNULL){perror(realloc);return0;}book-datap;book-capacitynext;return1;}staticintLoadContact(Contact*book){FILE*fpfopen(DATA_FILE,rb);if(fpNULL){if(errnoENOENT)// 首次运行还没有数据文件。return1;perror(fopen for reading);return0;}PeoInfo person;intok1;while(fread(person,sizeofperson,1,fp)1){if(!EnsureCapacity(book)){ok0;break;}book-data[book-size]person;}if(ferror(fp)){perror(fread);ok0;}if(fclose(fp)!0){perror(fclose after reading);ok0;}returnok;}staticintSaveContact(constContact*book){FILE*fpfopen(DATA_FILE,wb);if(fpNULL){perror(fopen for writing);return0;}size_twrittenbook-size0?0:fwrite(book-data,sizeof(PeoInfo),book-size,fp);intokwrittenbook-size;if(!ok)fputs(fwrite: incomplete save\n,stderr);if(fclose(fp)!0){perror(fclose after writing);ok0;}returnok;}staticvoidShowContact(constContact*book){printf(Total: %zu\n,book-size);for(size_ti0;ibook-size;i){constPeoInfo*pbook-data[i];printf(%s %d %s %s %s\n,p-name,p-age,p-sex,p-tele,p-addr);}}intmain(void){Contact book{0};charline[160];intchoice;if(!LoadContact(book)){free(book.data);return1;}for(;;){fputs(1 add, 2 show, 0 exit: ,stdout);if(fgets(line,sizeofline,stdin)NULL)break;if(sscanf(line,%d,choice)!1)continue;if(choice0)break;if(choice2){ShowContact(book);continue;}if(choice1){PeoInfo person{0};fputs(name age sex phone address (no spaces in fields): ,stdout);if(fgets(line,sizeofline,stdin)NULL)break;if(sscanf(line,%19s %d %4s %11s %29s,person.name,person.age,person.sex,person.tele,person.addr)!5){fputs(Invalid record.\n,stderr);continue;}if(!EnsureCapacity(book)){free(book.data);return1;}book.data[book.size]person;}}intsavedSaveContact(book);free(book.data);if(!saved)return1;puts(Saved.);return0;}我实际试了两次运行。第一次输入Alice 20 F 12345678901 Nanjing后退出第二次启动直接选“显示”还能看到同一条记录Total: 1 Alice 20 F 12345678901 Nanjing Saved.这份代码在本机用 MSBuild 编译通过两次运行的退出码都是 0测试目录里也生成了contact_demo.dat。我还试了空通讯录直接退出再启动后显示为 0 条。测试只说明同一台机器、同一个程序版本能把这份文件写回、读出换编译器或改结构体之后不能直接拿这个二进制文件通用。五、写完后给自己记的几句话这几组函数我现在按“数据的位置”来记标准流用scanf/printf传入的流用fscanf/fprintf字符串用sscanf/sprintf。读文件时先检查这一次读到了什么feof是读完后判断原因用的不能放在前面猜。通讯录这部分fread/fwrite的返回值是实际读写的元素个数。读少了要分清是 EOF 还是出错写少了就不能说保存成功。直接把结构体写进二进制文件做这次本机练习很方便如果以后想长期保存、换机器使用还得另想稳定的文件格式处理文件损坏和版本变化。参考资料WG14 C 标准草案、Microsoft C 运行库feof、Microsoft C 运行库fread。

相关推荐

C++11篇 (三) 深度解构:可变参数模板、类功能演进与 STL 的新版图
C++11篇 (三) 深度解构:可变参数模板、类功能演进与 STL 的新版图

目录 一,模板中的模板:可变参数模板 1.1从概念开始——什么是可变参数与参数包 1.1.1从C库中的printf引入 1.1.2模板的可变参数 Tips:新操作符追加:“sizeof…” 1.2简化代码的利器——可变参数模板该怎么用 1.2.1 递归函数… · 2026/9/24 5:01:43

JavaScript 函数:作用域、闭包与高阶函数
JavaScript 函数:作用域、闭包与高阶函数

在上一篇文章中,我们了解了 JavaScript 函数的基本概念——如何定义函数、传递参数、返回值,以及箭头函数的用法。 但会写函数、会调用函数,只是入门的第一步。 你有没有想过:为什么一个函数可以访问在它外面声明的变量&#xf… · 2026/9/24 5:01:37

高性能 Go JSON 库 json-iterator/go:vcluster 中的 100% 兼容序列化方案与源码解析
高性能 Go JSON 库 json-iterator/go:vcluster 中的 100% 兼容序列化方案与源码解析

云原生集群管理虚拟化多集群 【免费下载链接】vcluster vCluster creates tenant clusters: fully isolated environments delivered as managed Kubernetes, or as the foundation for Slurm, Ray, Run:ai and inference clusters. Each gets its own API server, CRDs and RB… · 2026/9/24 5:01:37

Nginx UI Node 配置完全指南:Name、Secret 与 SkipInstallation 的实战与源码解析
Nginx UI Node 配置完全指南:Name、Secret 与 SkipInstallation 的实战与源码解析

后端前端运维MCP 服务 【免费下载链接】nginx-ui Yet another WebUI for Nginx 项目地址: https://gitcode.com/gh_mirrors/ngi/nginx-ui 点击查看 免费下载 Nginx UI 从 v2.0.0-beta.37 起,将原本散落在 Server 配置节中的节点级选项收拢为独立的 [nod… · 2026/9/24 8:55:30

MTK黑砖修复原理与SP Flash Tool深度实践
MTK黑砖修复原理与SP Flash Tool深度实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 8:55:17

建模模拟气候驱动的富冰多年冻土边坡失稳
建模模拟气候驱动的富冰多年冻土边坡失稳

【科研速递】The Cryosphere | 建模模拟气候驱动的富冰多年冻土边坡失稳(2026-09-18) Modelling climate-induced instability of ice-rich permafrost slopes — — — — — 🌐 导语 近日,挪威奥斯陆大学(University… · 2026/9/24 8:54:59

Sliver Stager 分阶段投递实战指南:stage-listener 配置、Metasploit 集成与自定义 Stager 编写
Sliver Stager 分阶段投递实战指南:stage-listener 配置、Metasploit 集成与自定义 Stager 编写

网络安全 【免费下载链接】sliver Adversary Emulation Framework 项目地址: https://gitcode.com/gh_mirrors/sl/sliver 点击查看 免费下载 本篇技术指南完整讲解 Sliver(Adversary Emulation Framework)中的 Stager(分阶段投递… · 2026/9/24 8:54:53

AI办公工具实测:我用了一周的AI工作台,到底省了多少事
AI办公工具实测:我用了一周的AI工作台,到底省了多少事

最近把日常办公流程整体搬到了AI工作台上跑了一周,说说真实体验:哪些是噱头,哪些是真省时间。结论先放前面——对话式处理文档、会议纪要整理、多任务并行这三件事,AI办公台是真的快。 一、我测了什么场景 长文档总结:… · 2026/9/24 8:54:53

一文厘清技术中台、数据中台、业务中台与组织中台的区别与建设路径
一文厘清技术中台、数据中台、业务中台与组织中台的区别与建设路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 8:54:22

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码