=============================================================================
=============================================================================

涉及到的知识点有:

项目实战:做一个电子词典,快易通电子词典。

方案零:固定字典词条数目。(出现问题:多了的词条数解决不了,少了的词条数浪费内存。)

方案一:读2次文件,第一次读文件得到文件的词条数(每个循环读两行数据),第二次打开文件后根据文件实际的词条数来分配堆内存。

方案二:边读边随时增加struct dict在堆中的数量。

方案三:链表也可以实现。(基础班没有讲到哦!)

四种实现方案的效率比较

结构dict在内存中的分配说明图

=============================================================================
=============================================================================
08_dict.txt是个文本文件,汉字的格式是GBK格式的。

第一行
#a 代表一个词条。
第二行
Trans:art. 一;字母A 代表词条对应的翻译。
第三行
#a.m.
第四行
Trans:n. 上午
......
--------------------------------------
方案零:固定字典词条数目。

vs2017下示例代码:

  1 #define _CRT_SECURE_NO_WARNINGS
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 #include <string.h>
  5 #include <time.h>
  6
  7 #define MAX 111111 //最大记录数
  8
  9 struct dict
 10 {
 11     char *key;//key代表词条,是普通的指针,不是数组,目前还不是字符串,只是char类型的指针变量。
 12     char *content;//content代表词条的翻译,是普通的指针,不是数组,目前还不是字符串,只是char类型的指针变量。
 13 };
 14
 15
 16 void format_string(char *str)//去掉字符串结尾的回车和空格字符
 17 {
 18     size_t i;
 19     for(i = strlen(str) - 1; i >= 0; i--)//从右往左遍历字符串
 20     {
 21         if (str[i] != '\n' && str[i] != ' ' && str[i] != '\r')
 22         {
 23             str[i + 1] = '\0';
 24             break;
 25         }
 26     }
 27 }
 28
 29 //打开字典文件,并读取文件内容
 30 int open_dict(struct dict **p, const char *dict_filename)
 31 {
 32     FILE *pfile = fopen(dict_filename, "r");
 33     if (pfile == NULL)
 34         return 0;//打开文件失败,函数返回0。说明打开的文件不存在。目的是:为了避免乱操作、误操作,导致程序崩溃,这也是程序的健壮性。
 35
 36     //要在该函数open_dict内部给外部main函数的指针p来分配内存,怎么做呢?答:要把外部的指针的地址传递给open_dict,所以open_dict的参数需要用二级指针,到这是c语言常用的小技巧。
 37     //struct dict **p 给二级指针分配内存,用*p一级指针。
 38     *p = (struct dict *)malloc(sizeof(struct dict) * MAX);//固定分配MAX大小内存,执行完这句后,相当于main函数里面的p就指向了堆中的一块有效内存,该堆内存的大小为sizeof(struct dict) * MAX个字节。
 39     memset(*p, 0, sizeof(struct dict) * MAX);//将分配到的堆内存初始化为0
 40
 41     char buf[1024] = { 0 };
 42     size_t len = 0;
 43     int i = 0;//计数器
 44     while (!feof(pfile))//循环读取文件,直到文件末尾
 45     {
 46         memset(buf, 0, sizeof(buf));//读之前先把缓存buf清零(清空)。
 47         fgets(buf, sizeof(buf), pfile);//从文件读取文件一行
 48         len = strlen(buf);//得到读取到的字符串长度
 49         if (len > 0)//如果读到的这一行不是空行,就执行以下代码。
 50         {
 51             //*p相当于堆内存中的一个数组名。(*p)[i]的意思是:数组里面下标为i的结构变量。(*p)[i].key结构变量的成员key。
 52             (*p)[i].key = (char *)malloc(len);//根据字符串长度分配相应的堆内存
 53             memset((*p)[i].key, 0, len);//好习惯:任何分配出来的一块内存后,都要先把它清空(置零)后再使用。
 54             format_string(buf);//去掉字符串结尾的空格和回车
 55             strcpy((*p)[i].key, &buf[1]);//将读取到的内容拷贝到key中
 56             //free((*p)[i].key);//有专门的释放堆内存的函数。
 57         }
 58
 59         memset(buf, 0, sizeof(buf));
 60         fgets(buf, sizeof(buf), pfile);
 61         len = strlen(buf);
 62         if (len > 0)
 63         {
 64             (*p)[i].content = (char *)malloc(len);
 65             memset((*p)[i].content, 0, len);
 66             format_string(buf);
 67             strcpy((*p)[i].content, &buf[6]);
 68             //free((*p)[i].key);//有专门的释放堆内存的函数。
 69         }
 70
 71         i++;//计数器加1
 72     }
 73     fclose(pfile);//关闭字典文件
 74
 75     return i;//返回读取到的字典词条数
 76 }
 77
 78 //根据关键字key,在字典中查找内容
 79 int search_dict(const struct dict *p, int size, const char *key, char *content)
 80 {
 81     int i = 0;
 82     for (i = 0; i < size; i++)//遍历字典
 83     {
 84         //容错代码。
 85         if ((p[i].key == NULL) || (p[i].content == NULL))
 86             continue;
 87
 88         if (strcmp(p[i].key, key) == 0)
 89         {
 90             strcpy(content, p[i].content);
 91             return 1;//找到符合条件记录,返回1
 92         }
 93     }
 94     return 0;//没有找到符合条件记录,返回0
 95 }
 96
 97 //释放内存
 98 void free_dict(struct dict *p, int size)
 99 {
100     int i = 0;
101     for (i = 0; i < size; i++)//循环释放key与content成员内存
102     {
103         if (p[i].key)
104             free(p[i].key);
105         if (p[i].content)
106             free(p[i].content);
107     }
108     free(p);//释放p内存
109 }
110
111 int main(int argc, char *args[])
112 {
113     if (argc < 2)
114     {
115         printf("usage: %s dict-filename\n", args[0]);
116         return 0;//参数不足,程序退出
117     }
118     long start_ms = 0;//记录函数执行的开始时间
119     long end_ms = 0;//记录函数执行的结束时间
120     auto struct dict *p = NULL;
121     start_ms = clock();//得到系统当前时间,单位是ms。是long类型的一个值。
122
123     //要通过在函数open_dict的内部给main函数里面的指针p分配内存,需要传递指针p的地址给该函数open_dict,这是c语言里面一个最基本的技巧。
124     int dict_size = open_dict(&p, args[1]);//根据命令行第一个参数作为字典文件名,打开字典文件
125     if (dict_size == 0)
126         return 0;//打开字典文件失败,程序退出,表示要打开的文件不存在。目的是:为了避免乱操作、误操作,导致程序崩溃,这也是程序的健壮性。
127
128     end_ms = clock();//得到系统当前时间,单位是ms。是long类型的一个值。
129     printf("open_dict used %ld ms\n", end_ms - start_ms);//打印函数执行时间,单位:毫秒,是long类型的一个值。
130
131     char key[1024];
132     char content[1024];
133     while (1)
134     {
135         memset(key, 0, sizeof(key));
136         memset(content, 0, sizeof(content));
137         fgets(key, sizeof(key), stdin);//从键盘得到用户输入
138         format_string(key);//去掉字符串结尾的空格和回车
139         if (strncmp(key, "command=exit", 12) == 0)
140             break;
141
142         start_ms = clock();//得到系统当前时间,单位是ms。是long类型的一个值。
143         //dict_size是读取到的字典词条数。
144         if (search_dict(p, dict_size, key, content))//根据用户输入,在字典中检索
145         {
146             printf("%s\n", content);
147         } else
148         {
149             printf("not found\n");
150         }
151         end_ms = clock();//得到系统当前时间,单位是ms。是long类型的一个值。
152         printf("search_dict used %ld ms\n", end_ms - start_ms);//打印函数执行时间,单位:毫秒
153     }
154
155     start_ms = clock();
156     free_dict(p, dict_size);
157     end_ms = clock();
158     printf("free_dict used %ld ms\n", end_ms - start_ms);//打印函数执行时间,单位:毫秒
159     return 0;
160 }

-----------------------------------------------------------------------------
方案一:读2次文件,第一次读文件得到文件的词条数(每个循环读两行数据),第二次打开文件后根据文件实际的词条数来分配堆内存。

vs2017下示例核心代码:

......
......
//第一次读文件得到文件的词条数(每个循环读两行数据)。
int get_dict_size(FILE *pfile)//得到字典文件中词条总数
{if (pfile == NULL)return 0;int i = 0;char buf[2048];while (!feof(pfile)){fgets(buf, sizeof(buf), pfile);fgets(buf, sizeof(buf), pfile);i++;//读取两行后,计数器加1
    }return i;//执行过该函数后,指针到了文件的末尾了,为了能再次从文件中读出内容,需要在main函数的循环读取文件之前将指针设置在文件的开始位置。如下://fseek(pfile, 0L, SEEK_SET);//设置读取位置为字典文件的开始位置。
}//打开字典文件,并读取文件内容
int open_dict(struct dict **p, const char *dict_filename)
{FILE *pfile = fopen(dict_filename, "r");if (pfile == NULL)return 0;//打开文件失败,函数返回int dict_size = get_dict_size(pfile);//得到字典文件中词条总数if (dict_size == 0)return 0;//第二次打开文件后根据文件实际的词条数来分配堆内存。*p = (struct dict *)malloc(sizeof(struct dict) * dict_size);//根据字典文件词条总数分配内存memset(*p, 0, sizeof(struct dict) * dict_size);//将分配内存初始化为0char buf[2048] = { 0 };size_t len = 0;int i = 0;fseek(pfile, 0L, SEEK_SET);//设置读取位置为字典文件的开始位置while (!feof(pfile))//循环读取文件,直到文件末尾
    {memset(buf, 0, sizeof(buf));fgets(buf, sizeof(buf), pfile);//读取文件一行len = strlen(buf);//得到读取到字符串长度if (len > 0){(*p)[i].key = (char *)malloc(len);//根据字符串长度分配内存memset((*p)[i].key, 0, len);format_string(buf);//去掉字符串结尾的空格和回车strcpy((*p)[i].key, &buf[1]);//将读取到的内容拷贝到key中
        }memset(buf, 0, sizeof(buf));fgets(buf, sizeof(buf), pfile);len = strlen(buf);if (len > 0){(*p)[i].content = (char *)malloc(len);memset((*p)[i].content, 0, len);strcpy((*p)[i].content, &buf[6]);}i++;}fclose(pfile);//关闭字典文件return i;//返回读取到的字典词条数
}
......
......

-----------------------------------------------------------------------------
方案二:边读边随时增加struct dict在堆中的数量。

vs2017下示例核心代码:

......
......
//打开字典文件,并读取文件内容
int open_dict(struct dict **p, const char *dict_filename)
{FILE *pfile = fopen(dict_filename, "r");if (pfile == NULL)return 0;//打开文件失败,函数返回//一开始就分配一个结构体struct dict的堆内存。读完一个词条后再读的时候我再加一个词条的内存。*p = (struct dict *)malloc(sizeof(struct dict));memset(*p, 0, sizeof(struct dict));char buf[2048] = { 0 };size_t len = 0;int i = 0;while (!feof(pfile))//循环读取文件,直到文件末尾
    {memset(buf, 0, sizeof(buf));fgets(buf, sizeof(buf), pfile);//读取文件一行len = strlen(buf);//得到读取到字符串长度if (len > 0){(*p)[i].key = (char *)malloc(len);//根据字符串长度分配内存memset((*p)[i].key, 0, len);format_string(buf);//去掉字符串结尾的空格和回车strcpy((*p)[i].key, &buf[1]);//将读取到的内容拷贝到key中
        }memset(buf, 0, sizeof(buf));fgets(buf, sizeof(buf), pfile);len = strlen(buf);if (len > 0){(*p)[i].content = (char *)malloc(len);memset((*p)[i].content, 0, len);strcpy((*p)[i].content, &buf[6]);}i++;//边读边随时增加struct dict在堆中的数量。*p = (struct dict *)realloc(*p, (1 + i) * sizeof(struct dict));//把新增的堆内存清零(置零)。memset(*p + i, 0, sizeof(struct dict));}fclose(pfile);//关闭字典文件return i;//返回读取到的字典词条数
}
......
......

-----------------------------------------------------------------------------
方案三:链表也可以实现。(基础班没有讲到哦!)

vs2017下示例核心代码:
......
......
-----------------------------------------------------------------------------
结构dict在内存中的分配说明图如下所示:

=============================================================================
四种实现方案的效率比较:
如下图所示:

=============================================================================

我的GitHub地址:https://github.com/heizemingjun
我的博客园地址:http://www.cnblogs.com/chenmingjun
我的蚂蚁笔记博客地址:http://blog.leanote.com/chenmingjun
Copyright ©2018 黑泽明军
【转载文章务必保留出处和署名,谢谢!】

c语言基础学习12_项目实战:快译通字典相关推荐

  1. c语言基础学习11_项目实战:IDE(集成开发环境)

    ============================================================================= ====================== ...

  2. 2018年最新Vue从基础入门到项目实战视频教程网盘学习地址

    2018年最新Vue从基础入门到项目实战视频教程网盘学习地址: https://pan.baidu.com/s/15D6GiHnSgA5Eo0n9G5Ws1A 若网盘地址失效,可访问此地址(下单有网盘 ...

  3. Android零基础开发到项目实战

    Android零基础开发到项目实战(目录) 前言:本教程适合零基础学习安卓开发的伙伴,下面是目录,本博主会每天定时更新每一章节的教程,未完..... 一.Java基础阶段 day01_Java语言概述 ...

  4. 视频教程-20年Nodejs教程零基础入门到项目实战前端视频教程-Node.js

    20年Nodejs教程零基础入门到项目实战前端视频教程 7年的开发架构经验,曾就职于国内一线互联网公司,开发工程师,现在是某创业公司技术负责人, 擅长语言有node/java/python,专注于服务 ...

  5. 《软件测试技术大全:测试基础 流行工具 项目实战(第3版)》—第1章1.2节软件测试的发展...

    本节书摘来自异步社区<软件测试技术大全:测试基础 流行工具 项目实战(第3版)>一书中的第1章,第1.2节软件测试的发展,作者陈能技 , 黄志国,更多章节内容可以访问云栖社区"异 ...

  6. react从零基础入门到项目实战视频教程

    React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站, 并于 2013年 5 月开源.React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它.这 ...

  7. java跳转控制语句有哪些_Java语言基础学习之流程控制语句和跳转控制语句实例分析...

    本文主要向大家介绍了Java语言基础学习之流程控制语句和跳转控制语句实例分析,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助. 一.流程控制语句 在一个程序执行的过程中,各条语句的执行顺 ...

  8. 01_Go语言基础学习_Golang语言特性、环境搭建、第一个Go程序、包

    1. Golang语言特性: 垃圾回收: 1.内存自动回收,再也不需要开发人员管理内存: 2.开发人员专注业务实现,降低了心智负担 : 3.只需要new分配内存,不需要释放 天然并发: 1.从语言层面 ...

  9. 《C语言基础学习笔记》—— 黑马程序员 <Part1-4>

    <C语言基础学习笔记 Part1-4> Part 1 基础介绍 1. 第一个C语言程序:HelloWorld 1.1 编写C语言代码:hello.c 1.2代码分析 1)include 头 ...

最新文章

  1. git add 回滚_Git的reset命令详解
  2. linux安装google chrome
  3. Windows核心编程 第十七章 -内存映射文件(上)
  4. 【算法数据结构Java实现】Java实现单链表
  5. 受限玻尔兹曼机(Restricted Boltzmann Machine)分析
  6. Win7 不能安装SQL Server 2005 Management Studio Express.msi解决办法
  7. 计组之存储系统:8、Cache写策略(全写法、写回法、写分配法、非写分配法、多级Cache)
  8. Python办公自动化|光速对比并提取两份Word/Excel中的不同元素
  9. 搭建卷积神经网络怎么确定参数_汽车冲压模具中拉延件各参数要怎么确定?本文教你确定方法!...
  10. SAP License:ERP学习的一些个人建议
  11. 雷林鹏分享:Ruby 发送邮件 - SMATP
  12. 富文本编辑器(UEditor)的使用
  13. 计算机软件技术实习——迷宫算法核心
  14. Azure云端迁移4种方式
  15. 企业如何避交所得税__公司如何才能少交企业所得税
  16. 每日英语(2021-2-27)
  17. 微信小程序海报功能(canvas)- - -附效果图
  18. Safari Extension 扩展插件中关于权限的二三事(例如设置权限、权限类型等)
  19. xshell4连接ubuntu,报错No matching outgoing encryption
  20. java GUL编程

热门文章

  1. ARP SpoofDoS攻击编程(转)
  2. uniapp 微信小程序 web-view上传手机文件管理器里最近文件(不限文件类型)
  3. QTableWidget中表格显示图片
  4. Pyth笔记-高级装饰器
  5. 计算机科学数理逻辑考试,计算机科学中使用的数理逻辑_期末考试题2006到2014.pdf...
  6. 5分绩点转4分_8点不起床扣5分,大学生还有“睡眠自由”吗?
  7. css字体大小随控件,GitHub - Luren3/CssTextView: 这是基于TextView的扩展小控件,一个支持设置一段文字设置不同的颜色、大小、字体样式以及点击事件...
  8. 充电桩用什么写软件_小区安装汽车充电桩,是选择直流桩还是交流充电桩,有什么区别?...
  9. 智沃邦条码扫码出入库管理系统 仓库出入库管理系统 一维码 二维码系统
  10. UE4 BP节点 :相对位置,相对坐标,相对方向(Inverse Transform Direction /Location/Rotation)