C++课程设计——背单词程序

C++课程设计——背单词程序

设计要求
设计一背单词程序,程序以菜单方式工作,字典使用文本文件存放就可以了。使之能提供以下功能:添加新词 支持顺序测验和随机测验 记录没背过的单词 查找单词的汉语或英语意思(输入中文查对应的英语意思,输入英文查对应的汉语意思)

1.课程设计目的

1.1 前言

背英语单词是最令人头疼的事儿,如果能选择一套适合的背单词软件,对于单词记忆会有事半功倍的效果,然而目前市场上背单词软件到处都是,我们应该选择什么软件呢,这确实是件另人头疼的事,选到好的,对我们的学习也许有很大帮助。因此,设计了一款针对性的背单词程序,用户使用时可以根据自己的水平构造适合自己的词库。该程序设计了基本的增、删、改、查功能,在查询部分细分了4大类——英译汉、汉译英、浏览没背过的生词、浏览整个词典。

值得注意的是,该程序会按照英语词典顺序对用户录入的单词和翻译进行重新排列。比如:录入时的顺序时d,c,a,e。而用户再次浏览时的顺序是a,b,c,d。此外,用户可以利用程序的顺序测验功能进一步按照词典顺序进行背单词。用户可以利用随机测验功能进行单词考试(出题是系统随机出不同的题)。

1.2功能性需求分析

背单词程序包含了许多C++知识点,C++的特色就是类的运用。首先是先定义一个类来进行对数据成员和一系列成员函数进行声明定义,然后以菜单界面的方式来进行调用成员函数进行功能的实现。我对于这个项目构造了以下功能:
(1)添加单词 用户在输入英文后,还需要输入对应的翻译

(2)删除单词 用户输入想删除的英文,系统会把这个单词从文件里删除

(3)修改单词 当英文和对应的汉语不相符时,用户可以先输入该英文,再输入正确的其汉语

(4)顺序测验 用户进入该界面后,会按照词典的顺序进行记忆背诵单词,同时系统还会统计用户记忆了多少个单词

(5)随机测验 用户进入该界面后,会按照系统随机给出的5个选择题进行作答。用户需要对每道题进行选择A或B或C或D,答题完毕后,系统会给出分数和评估用户的成绩是否合格,是否优秀。

(6)查看词典 用户如果选择5,会进入二级菜单(1.英译汉 2.汉译英3.浏览全部4.查看未背过的单词)。针对英译汉,用户输入英文,系统会自动给出该英文对应的汉语意思,汉译英同理。当用户查看未背过的单词时,需要用户输入已经记住的单词个数,再输入想查看的生单词个数。

(7)退出系统 当用户输入7,就会退出背单词程序

1.3 非功能性需求分析

为用户提供更好的使用感受,对程序进行美化:
(1)调用了EaxyX图形库,创建了一个窗口,同时显示它的控制台。
(2)使用了system(“color F5”);显示颜色控制函数,设置字体(前景色)和背景颜色,详细颜色设置说明如图1-1。F代表背景色黑色,5代表字体颜色。

图1-1
图1-1 详细颜色设置说明

(3)运行界面友好,易于操作,输入方式简单明了易于操作、安全可靠。
(4)对输入错误的情况进行提示。
(5)背单词针对的是英语,所以单词信息包括英文和对应的翻译
string english;
string chinese;

2.课程设计题目描述和要求

2.1概要设计

1.本程序所要实现的基本功能有添加单词、查看生词、英译汉、汉译英、顺序测验、随机测验。另外设计了删除单词、修改单词、浏览全部单词便于对文件里的数据进行完善。

2.设计针对特定功能的数据结构和类:
WordS类存放单词的英文和中文,整个词库则使用STL中的vector向量来存储,即将存在文件里的单词数据转存到vector容器中。
Data类中声明和实现对单词的操作——增、删、改、查、统计、判断是否合规。
MyFrame类中声明和实现在菜单中的功能。
Myless类实现辅助排序功能。
在本设计中为了保持每个类的功能清晰、简洁,在单个类内部实现的功能应当有一定的联系,非对外的接口设置为private属性;

3.程序的具体实现,涉及的算法主要包括二分法查找单词、词库排序

2.2程序设计组成框图

程序设计组成框图如图2-1所示
在这里插入图片描述
图2-1背单词程序组成框图

2.3 背单词程序流程图

背单词流程图,如图2-2所示
在这里插入图片描述
图2-2 背单词流程图

2.4类图

1.Data类图如图2-4-1所示在这里插入图片描述
图2-4-1 Data类图
2.MyFrame类图如图2-4-2所示
在这里插入图片描述
图2-4-2 MyFrame类图
3.WordS类图如图2-4-3所示
在这里插入图片描述
图2-4-3 WordS类图

4.myless类图如图2-4-4所示
在这里插入图片描述
图2-4-4 myless类图

3.课程设计报告内容

3.1详细设计

3.1.1定义一个WordS类

WordS类用来表示每一个单词的信息内容——有英语单词和对应的中文翻译两个内容,用两个私有数据成员来进行表示:
		private:
	string english; //英文单词
	string chinese; 	//对应的中文翻译
同时为了方便其他类对单词的内容进行操作,还提供了一系列比较简单基础的公有操作:
public:
	WordS();//默认空构造函数
	WordS(string english, string chinese); //给出英文单词和中文解释的构造函数
	string getEnglish();//获取英文内容
	string getChinese();//获取中文内容
	void setEnglish(string english); //重新设置英文内容
void setChinese(string chinese); //重新设置中文内容

3.1.2定义一个Data类

Data类是一个存放数据的类,用来储存录入的单词的信息内容,并把所有单词包含的内容放在一个vector<WordS>中(Vector容器的详细解释见附录),该数据段被定义为privateprivate:
	vector<WordS> words; //存放单词,向量内的元素为WordS类对象	
WordS wordNode;//vector是向量类型,可以容纳许多类型的数据,因此也被称为容器
	//(可以理解为动态数组,是封装好了的类)
	//进行vector操作前应添加头文件#include <vector>
	//定义具有10个WordS元素的向量(尖括号为元素类型名,它可以是任何合法的数据类型),不具有初值,其值不确定//vector<WordS>a(10);

另外提供对外的接口,可以对单词表进行一系列操作:
public:
	Data();//默认构造函数
	int getDicSize();//返回单词的总数目
	bool isEmpty();//判断字典是否为空
	WordS getWordNode(int i); //返回下标为i的单词,i不合法则返回空
	int setWordNode(int i, WordS newWordNode); //修改第i个WordS节点,失败则返回值为负数
	int addWordNode(WordS wordNode); //添加一个新单词
	int deleteWordNode(int i); //删除第i个WordS节点,失败则返回值为负数
	int binarySearch(string word); //根据单词二分查找相应的条目,查找失败返回负数
	int binarySearchr(string word); //根据单词二分查找相应的条目,查找失败返回负数
	void sortWords();	//重新对单词按字典序进行排序

3.1.3定义一个myless类

myless类是为了方便调用sort()函数,重载()运算符,保证录入文件里的单词是按照词典顺序来排列的,参数是两个WordS类的对象。
public:
	bool operator()(WordS a, WordS b) {
    
    //重载运算符,为排序做准备
	return a.getEnglish() < b.getEnglish();//按照字典序排列  按照字母顺序//return 1 > 2; 返回false
	}
3.1.4定义一个MyFrame类
MyFrame类主要控制系统的界面,有一个Date类的实例dateBase作为私有的数据成员,用来保存所有单词的内容数据。
private:  Data dataBase; //相关的内容数据库	

同时MyFrame类提供了一系列公有方法来实现与背单词相关的功能。如下:
public:
	MyFrame();//空构造函数
	void initialize();//初始化整个界面
	void addWord();//添加新单词
	void deleteWord();//删除已有的单词
	void modifyWord();//修改现有的单词
	void browseDic();	//浏览词典
	void exitSystem();//退出系统
	void exam();//模拟考试
	void exercise();//背单词练习
	void konghang(int n); //控制格式,输出一些空行

3.1.4 main()函数

main()函数里面主要调用了EaxyX库用来对程序进行美化,如下:
initgraph(640,450,SHOWCONSOLE);//创建一个窗口 显示控制台
setbkcolor(BROWN);//背景颜色为棕色1
cleardevice();//清屏2
settextstyle(50,0,L"楷体");//设置文字样式,大小,字体
setbkmode(TRANSPARENT);//设置背景模式trans parent透明
settextcolor(RGB(34,633,82));
outtextxy(50, 50, L"欢迎使用背单词小程序");
MyFrame frame;//生成一个界面对象
frame.initialize();//初始化界面并将控制转移到该对象内部
getchar();//使窗口停留
closegraph();//关闭窗口

3.1.6主要功能

1.单词表的实现
在Data类的构造函数中借助ifstream实现构建单词表的功能。首先实例化一个ifstream类,然后打开文件,读入相应的单词记录同时实例化一个WordS类,最后将该对象放入vector容器中并关闭文件:
	ifstream file("dicA.txt");//打开文件
	  while (file >> english >> chinese) {
    
    
		this->wordNode.setChinese(chinese);
		this->wordNode.setEnglish(english);
		(this->words).push_back(this->wordNode); //words表示实例化一个WordS类,wordNode表示单词信息(英文+中文)
	}
file.close();//关闭文件

2.增、删、改功能的实现
增加单词由Data类中的addWordNode(WordS wordNode)函数实现,首先放到容器vector中,再进行重新排序:
int Data::addWordNode(WordS wordNode) {
    
    
	this->words.push_back(wordNode);
	//在 words尾部添加wordNode
	//vector<WordS> words;
	// words.push_back(wordNode);
	this->sortWords();
	return 0;
}

删除节点,首先需要根据给出的单词进行二分查找,然后通过Data类中的deleteWordNode(int i)函数来实现,二分查找的基本原理是利用序列的有序性,每次将查找范围折半,直到确定找到目标或者目标不存在:
//删除第i个WordNode节点,失败则返回值为负数
int Data::deleteWordNode(int i) {
    
    
	if ((i < 0) || (i >= (int)(this->words.size()))) {
    
    
		//cout<<"下标不合法!"<<endl;
		return -1;
	}
	this->words.erase((this->words.begin()) + i);
	return 1;
}

//根据单词二分查找相应的条目,查找失败返回空
int Data::binarySearch(string word) {
    
    
	int left = 0;
	int right = this->getDicSize() - 1; //进行二分查找
	while (left <= right) {
    
    
		int middle = (left + right) / 2; //中间元素
		if (this->words[middle].getEnglish() == word) //找到目标
			return middle;
		else if (this->words[middle].getEnglish() > word) //目标在左半部分
			right = middle - 1; 
		else left = middle + 1; //目标在右半部分
	}
	return -1;
	//cout<<"查找失败!"<<endl;
	//return *(new WordNode());
}

修改单词的功能也只需找到对应的英文,然后修改翻译部分的中文内容,由于单词的内容没有发生变化,因此不需要重新排序。

//修改第i个WordNode节点,失败则返回值为负数
int Data::setWordNode(int i, WordS newWordNode) {
    
    
	if ((i < 0) || (i >= this->getDicSize())) {
    
    
		//cout<<"下标不合法!"<<endl;
		return -1;
	}
	this->words[i] = newWordNode;
	this->sortWords();
	return 1;
}

3.顺序测验、随机测验、浏览没背过的单词

(1)在按顺序背单词的界面实现了顺序测验,此时的顺序并非录入的顺序,是按照词典顺序进行背诵,对背诵的单词进行计数的目的是为了浏览没背过的单词。
核心代码如下:
int number = this->dataBase.getDicSize();//统计单词表中所有的单词个数
		for (int i = 0; i < number; i++) {
    
    
		cout << "单词:   " << this->dataBase.getWordNode(i).getEnglish() << endl;
		cout << endl;//按照单词下标找出单词对应的在中文和英文
		cout << "解释:   " << this->dataBase.getWordNode(i).getChinese() << endl;
	cout << "按照单词表顺序,你已经记住的单词数:" << i+1 <<endl;

(2)在模拟考试的界面实现了随机测验,生成随机数取出相应单词呈现给用户,核心代码如下: 
srand(unsigned(time(0))); //防止每次运行生成的随机数相同
	for (int i = 0; i < 5; i++) {
    
    //修改练习题目,随机选取5个单词
		int tmp = rand() % (this->dataBase.getDicSize());
		bool repeat = false;
		for (int j = 0; j < i; j++) {
    
    //检查随机生成的单词是否重复
			if (number[j] == tmp)
				repeat = true;
		}
		if (repeat == true)
			i--;
		else
			number[i] = tmp;
	}
(3)浏览没背过的单词,详细代码如下:
else if (order == 4) {
    
    
		int begin;
		int length;
		cout << "请输入记过的单词个数:";
		cin >> begin;
		cout << "请输入要查看的单词个数:";
		cin >> length;
		if ((begin < 0) || (length <= 0) || (begin + length) > this->dataBase.getDicSize()) {
    
    
			cout << "输入无效!无法满足查看要求!" << endl;
			system("pause");
			return;
		}
		system("cls");
		for (int i = 0; i < length; i++) {
    
    
			cout << this->dataBase.getWordNode(begin + i).getEnglish()
				<< "   " << this->dataBase.getWordNode(begin + i).getChinese() << endl;
		}
		system("pause");
		return;
	}

3.2执行结果

见附录

3.3调试与测试

  1. 3.3.1进行EaxyX图形编程时错误
    在这里插入图片描述
    参数错误,找不到对应的函数,error C2665: “settextstyle”: 4 个重载中没有一个可以转换所有参数类型,是由于字符集导致食物,解决方案:
    1.在字符串前面加上大写的L
    2.用TEXT()把字符串包起来,_T(),原理同上
    3.不需要添加任何代码,进项目->属性->配置属性->常规->字符集->改为多字节字符集,推荐使用。
    在程序中settextstyle(50,0,“楷体”);这个是错误语句。这里我用的是方案1,改为settextstyle(50,0,L"楷体");成功运行

  2. 随机测验函数出错
    第一步设计的由用户设置要训练的题目个数,但是出现了下标不合法的情况,如图3-3-2 下标不合法。
    原因分析:因为词库的单词数量小,创建的数组int number[100]又太大了。导致随机产生的数字找不到对应的下标。随后对这部分代码进行改变,在代码里由管理员设置好训练的题目个数,用户直接按照事先设置好的题目个数进行随机测验。

在这里插入图片描述

4.总结

在这次程序编制的过程中出现了很多问题,主要原因是自己以前接触的关于编制程序的知识的缺乏,通过背单词程序的开发让我知道了自己的不足和缺点,写出一个好的程序需要自己掌握更多的知识,学习他人优秀的代码。一个好的程序员一定是从程序堆里走出来的,只有通过参与具体的程序开发才知道自己知识的有限,才会学会具体的分析问题,才会对自己的逻辑性和层次性得到更好的锻炼。

课程设计是一门专业课,让我学习到了很多的专业知识,也让我的专业技能得到了提升,同时又是一门思辨课,让我感触很深,让我对抽象的理论知识有了具体的认识,在这次课程设计中学到了很多以前没有接触的知识。更是对复杂的知识点有了更深的理解,比如getline()函数、容器。还学到了一些算法知识,比如二分法查找。但是对图形库的知识还是不够掌握,这次是先了解了EaxyX图形库,还有qt库等着我去学习。在以后的学习中,自己要加强程序的锻炼,多参考好的程序和各种各样的函数,为设计出功能齐全并且界面美观的程序。

5.附录及参考资料

5.1 vector容器

一、介绍
vector (向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的。
vector的构造函数,函数原型如下:
1.vector v ; //使用模板类,默认构造函数
2.vector(v.begin(),v.end()); //将[v.begin(),v.end())区间中的元素拷贝给本身
3.vextor(n,elem); //将n个elem拷贝给本身
4.vector(const vector &v) ; //拷贝构造函数
vector和普通数组的区别:数组是静态的,长度不可改变,而vector可以动态扩展,增加长度;数组内数据通常存储在栈上,而vector中数据存储在堆上。注:动态扩展并不是在原空间之后续接新空间,而是找到比原来更大的内存空间,将原数据拷贝到新空间,释放原空间。

二、用法:在程序开头处加上#include

三、变量声明

声明一个int向量以替代一维的数组:vector a;(等于声明了一个int数组a[],大小没有指定,可以动态的向里面添加删除)。
用vector代替二维数组.其实只要声明一个一维数组向量即可,而一个数组的名字其实代表的是它的首地址,所以只要声明一个地址的向量即可,即:vector <int > a。同理想用向量代替三维数组也是一样,vector <int*>a;再往上面依此类推。举例如下:
vector vec; //声明一个int型向量
vector vec(5); //声明一个初始大小为5的int向量
vector vec(10, 1); //声明一个初始大小为10且值都是1的向量
vector vec(tmp); //声明并用tmp向量初始化vec向量
vector tmp(vec.begin(), vec.begin() + 3); //用向量vec的第0个到第2个值初始化tmp
int arr[5] = {1, 2, 3, 4, 5};
vector vec(arr, arr + 5);//将arr数组的元素用于初始化vec向量
//说明:当然不包括arr[4]元素,末尾指针都是指结束元素的下一个元素,
//这个主要是为了和vec.end()指针统一。
vector vec(&arr[1], &arr[4]); //将arr[1]~arr[4]范围内的元素作为vec的初始值

三.基本操作

1.容量
向量大小,即容器中元素的个数: vec.size();
向量最大容量: vec.max_size();
更改向量大小: vec.resize();
向量真实大小: vec.capacity();
向量判空: vec.empty();
减少向量大小到满足元素所占存储空间的大小: vec.shrink_to_fit(); //shrink_to_fit
2.修改
多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值
末尾添加元素: vec.push_back();
末尾删除元素: vec.pop_back();
任意位置插入元素: vec.insert();
任意位置删除元素: vec.erase();
vec1.erase(vec1.begin(),vec1.end());//删除之间的元素,其他元素前移
交换两个向量的元素: vec.swap();
清空向量元素: vec.clear();
3.迭代器
开始指针:vec.begin();
末尾指针:vec.end(); //指向最后一个元素的下一个位置
指向常量的开始指针: vec.cbegin(); //意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。
指向常量的末尾指针: vec.cend();
4.元素的访问
下标访问: vec[1]; //并不会检查是否越界
at方法访问: vec.at(1); //以上两者的区别就是at会检查是否越界,是则抛出out of range异常
访问第一个元素: vec.front();
访问最后一个元素: vec.back();
返回一个指针: int* p = vec.data(); //可行的原因在于vector在内存中就是一个连续存储的数组,所以可以返回一个指针指向这个数组。这是是C++11的特性。

5.2 rand()函数和srand()函数

一、rand()函数
rand()函数用来产生随机数,但是,rand()的内部实现是用线性同余法实现的,是伪随机数,由于周期较长,因此在一定范围内可以看成是随机的。rand()会返回一个范围在0到RAND_MAX(至少是32767)之间的伪随机数(整数)。
所以,在调用rand()函数之前,可以使用srand()函数设置随机数种子,如果没有设置随机数种子,rand()函数在调用时,自动设计随机数种子为1。随机种子相同,每次产生的随机数也会相同。
·rand()函数需要的头文件是:<stdlib.h>
·rand()函数原型:int rand(void);
例:使用rand()函数产生1-100以内的随机整数:int number1 = rand() % 100+1;

二、srand()函数
·srand()函数需要的头文件仍然是:<stdlib.h>
·srand函数是随机数发生器的初始化函数。原型:void srand(unsigned seed);
用法:它初始化随机种子,会提供一个种子,这个种子会对应一个随机数,如果使用相同的种子后面的rand()函数会出现一样的随机数,如: srand(1); 直接使用1来初始化种子。不过为了防止随机数每次重复,常常使用系统时间来初始化,即使用 time函数来获得系统时间,它的返回值为从 00:00:00 GMT, January 1, 1970 到现在所持续的秒数,然后将time_t型数据转化为(unsigned)型再传给srand函数,即: srand((unsigned) time(&t)); 还有一个经常用法,不需要定义time_t型t变量,即: srand((unsigned) time(NULL)); 直接传入一个空指针,因为你的程序中往往并不需要经过参数获得的数据。
进一步说明下:计算机并不能产生真正的随机数,而是已经编写好的一些无规则排列的数字存储在电脑里,把这些数字划分为若干相等的N份,并为每份加上一个编号用srand()函数获取这个编号,然后rand()就按顺序获取这些数字,当srand()的参数值固定的时候,rand()获得的数也是固定的,所以一般srand的参数用time(NULL),因为系统的时间一直在变,所以rand()获得的数,也就一直在变,相当于是随机数了。只要用户或第三方不设置随机种子,那么在默认情况下随机种子来自系统时钟。如果想在一个程序中生成随机数序列,需要至多在生成随机数之前设置一次随机种子。 即:只需在主程序开始处调用srand((unsigned)time(NULL)); 后面直接用rand就可以了。不要在for等循环放置srand((unsigned)time(NULL));
例:使用rand()和srand()产生1-100以内的随机整数:
srand(time(0));
int number1 = rand() % 100+1;

三、使用rand()和srand()产生指定范围内的随机整数的方法
“模除+加法”的方法
因为,对于任意数,0<=rand()%(n-m+1)<=n-m
因此,0+m<=rand()%(n-m+1)+m<=n-m+m
因此,如要产生[m,n]范围内的随机数num,
可用:int num=rand()%(n-m+1)+m;其中的rand()%(n-m+1)+m算是一个公式,记录一下方便以后查阅。比如产生10~30的随机整数:srand(time(0)); int a = rand() % (21)+10;

5.3 二分法查找

一、思想:举个例子——猜字游戏;
随机写一个0到99之间的数字,然后猜猜写出的数字。猜的过程中,每猜一次,只会告诉您猜的大了还是小了,知道猜中为止。假设写出的数字是23,你可以按照下面的步骤来试一试。(如果猜测范围的数字有偶数个,中间数有两个,就选择较小的那个)。如图5-2 详细展示
在这里插入图片描述

7次就猜出来了,是不是很快,这个例子用的是二分思想,按照这个思想,即便猜的是0到999的数字,最多也只要10次就能猜中。
利用二分法思想,每次都与区间的中间数据对比大小,缩小查找区间的范围。为了更加直观,用一张查找过程的图。其中low和high表示带查找区间的下标,mid表示查找区间的中间元素下标。如图5-3 详细展示
在这里插入图片描述

low、high、mid都是数组下标,其中low、high表示当前查找的区间范围,初始low=0,high=n-1.mid表示[low,high]的中间位置。通过对比a[mid]与value的大小,来更新接下来要查找的区间范围,直到找到或者区间缩小为0,就退出。这里有三个容易出错的地方:
1、循环退出条件,注意是low<=high,而不是low<high。
2、mid的取值, 实际上,mid=(low+high)/2这种写法是有问题的。因为如果low和high比较大的话,两者之和就有可能会溢出。改进的地方是将mid的计算方式写成low+(high-low)>>1)。因为相比除法运算来说,计算机处理位运算要快得多。
3、low和high的更新。Low=mid+1,high=mid-1。注意这里+1和-1,如果直接写成low=mid或者high=mid,就可能发生死循环。比如,当high=3,low=3时,如果a[3]不等于value,就会导致一直循环不退出。

二、二分查找应用场景的局限性
1、二分查找依赖的是顺序表结构,简单点说就是数组
二分法查找算法需要按照下标随机访问元素,只能用在数据通过顺序表来储存的数据结构上。如果数据是通过其他数据结构存储的,则无法应用二分查找。
2、二分查找针对的是有序数据
二分查找要求数据必须是有序,主要适用于一组静态的数据,没有频繁地插入、删除,我们就可以进行一次排序,多次二分查找的场景中。
3、数据量太小不适合二分查找
如果处理的数据量很小,完全没有必要用二分查找,顺序遍历就足够了。不过如果数据间的比较操作非常耗时,不管数据量的大小,使用二分法查找比较合适。
4、数据量太大也不适合二分法查找
二分法的底层需要依赖数组这种数据结构,而数组为了支持随机访问的特性,要求内存空间连续,对内存的要求比较苛刻。二分法查找是作用在数组这种结构之上的,所以太大的数据用数组存储就比较吃力,也就不用二分查找了。

5.4 getline()函数

getline是C++标准库函数;它有两种形式,一种是头文件< istream >中输入流成员函数;一种在头文件< string >中普通函数;它遇到以下情况发生会导致生成的本字符串结束:(1)到文件结束,(2)遇到函数的定界符,(3)输入达到最大限度。
一、 输入流成员函数getline()函数语法结构
在< istream >中的getline()函数有两种重载形式:s: 进行读入操作的输入流;num 存储读入的内容;delim 终结符

·istream &getline( char *sr, streamsize num, char delim );
·istream &getline( char *s, streamsize num );

作用是: 从istream中读取至多num个字符(包含结束标记符)保存在s对应的数组中。即使还没读够num个字符,如果遇到delim 或 字数达到限制,则读取终止,delim都不会被保存进s对应的数组中。

二、普通函数getline()
函数语法结构:在< string >中的getline函数有四种重载形式:is :表示一个输入流,例如 cin。str :string类型的引用,用来存储输入流中的流信息。
delim :char类型的变量,所设置的截断字符;在不自定义设置的情况下,遇到’\n’,则终止输入。

·istream& getline (istream&  is, string& str, char delim);
·istream& getline (istream&& is, string& str, char delim);
·istream& getline (istream&  is, string& str);
·istream& getline (istream&& is, string& str);

用法和上一种类似,但是读取的istream是作为参数is传进函数的。读取的字符串保存在string类型的str中。
1.cin.getline()函数的完整形式有三个参数:cin.getline(字符数组名,字符个数,结束标志)
注意:若指定参数“字符个数”为n,则利用cout函数输出时只显示字符数组中的前n-1个字符。原因:字符数组的第n个字符是不可见字符’\0’

2.当第三个参数省略时,系统默认为’\0’
3.简单实例:

· cin.getline(str,8,'m'),当输入abcdefghijklmn时,输出abcdefg,因为第8位是不可见字符'\0'
· cin.getline(str,8,'e'),当输入abcdefghijklmn时,输出abcd

6.代码

WordS.h

#pragma once
#ifndef WORDNODE_H
#define WORDNODE_H

#include<iostream>
#include<string>
#include<vector>

using namespace std;

class WordS {
    
    
public:
	//默认空构造函数
	WordS();
	//给出英文单词和中文解释的构造函数
	WordS(string english, string chinese);
	//获取英文内容
	string getEnglish();
	//获取中文内容
	string getChinese();
	//重新设置英文内容
	void setEnglish(string english);
	//重新设置中文内容
	void setChinese(string chinese);
private:
	//英文单词
	string english;
	//对应的中文语义
	string chinese;
};

#endif

WordS.cpp

#include<iostream>
#include<string>
#include<vector>
#include "WordS.h"

using namespace std;

//默认空构造函数
WordS::WordS() {
    
    
}

//给出英文单词和中文解释的构造函数
WordS::WordS(string english, string chinese) {
    
    
	this->english = english;
	this->chinese = chinese;
	
}

//获取英文内容
string WordS::getEnglish() {
    
    
	return this->english;
}

//获取中文内容
string WordS::getChinese() {
    
    
	return this->chinese;
}

//重新设置英文内容
void WordS::setEnglish(string english) {
    
    
	this->english = english;
}

//重新设置中文内容
void WordS::setChinese(string chinese) {
    
    
	this->chinese = chinese;
}

data.h

#pragma once
#ifndef DATABASE_H
#define DATABASE_H

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include "WordS.h"

using namespace std;

//存放与单词相关的数据
class Data {
    
    
public:
	//默认构造函数
	Data();
	//返回单词的总数目
	int getDicSize();
	//判断字典是否为空
	bool isEmpty();
	//返回下标为i的单词,i不合法则返回空
	WordS getWordNode(int i);
	//修改第i个WordS节点,失败则返回值为负数
	int setWordNode(int i, WordS newWordNode);
	//添加一个新单词
	int addWordNode(WordS wordNode);
	//删除第i个WordS节点,失败则返回值为负数
	int deleteWordNode(int i);
	//根据单词二分查找相应的条目,查找失败返回负数
	int binarySearch(string word);
	//根据单词二分查找相应的条目,查找失败返回负数
	int binarySearchr(string word);
	//重新对单词按字典序进行排序
	void sortWords();

private:
	//存放单词,向量内的元素为WordS类对象
	

	//vector是向量类型,可以容纳许多类型的数据,因此也被称为容器
	//(可以理解为动态数组,是封装好了的类)
		//进行vector操作前应添加头文件#include <vector>
	//定义具有10个WordS元素的向量(尖括号为元素类型名,它可以是任何合法的数据类型),不具有初值,其值不确定
	//vector<WordS>a(10);
    vector<WordS> words;
	WordS wordNode;
};

#endif

data.cpp

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<fstream>
#include "data.h"
#include "WordS.h"
#include "myless.cpp"

using namespace std;
//默认构造函数
Data::Data() {
    
    
	string english;
	string chinese;
	
	//打开词典文件
	ifstream file("dicA.txt");
	while (file >> english >> chinese) {
    
    
		this->wordNode.setChinese(chinese);
		this->wordNode.setEnglish(english);
		(this->words).push_back(this->wordNode);//将相应节点插入数据库中

	}
	//关闭文件
	file.close();
}

//返回单词的总数目
int Data::getDicSize() {
    
    
	return this->words.size();
}

//判断字典是否为空
bool Data::isEmpty() {
    
    
	if ((this->getDicSize()) <= 0)
		return true;
	return false;
}

//返回下标为i的单词,i不合法则返回空
WordS Data::getWordNode(int i) {
    
    
	if (i < 0 || (i >= (this->getDicSize()))) {
    
    
		cout << "下标不合法!" << endl;
		return *(new WordS());
	}
	return this->words[i];
}

//修改第i个WordNode节点,失败则返回值为负数
int Data::setWordNode(int i, WordS newWordNode) {
    
    
	if ((i < 0) || (i >= this->getDicSize())) {
    
    
		//cout<<"下标不合法!"<<endl;
		return -1;
	}
	this->words[i] = newWordNode;
	this->sortWords();
	return 1;
}

//添加一个新单词
int Data::addWordNode(WordS wordNode) {
    
    
	this->words.push_back(wordNode);
	
	//在 words尾部添加wordNode
	//vector<WordS> words;
	// words.push_back(wordNode);
	
	this->sortWords();
	return 0;
}

//删除第i个WordNode节点,失败则返回值为负数
int Data::deleteWordNode(int i) {
    
    
	if ((i < 0) || (i >= (int)(this->words.size()))) {
    
    
		//cout<<"下标不合法!"<<endl;
		return -1;
	}
	this->words.erase((this->words.begin()) + i);
	return 1;
}

//根据单词二分查找相应的条目,查找失败返回空
int Data::binarySearch(string word) {
    
    
	int left = 0;
	int right = this->getDicSize() - 1;
	//进行二分查找
	while (left <= right) {
    
    
		//中间元素
		int middle = (left + right) / 2;
		//找到目标
		if (this->words[middle].getEnglish() == word)
			return middle;
		//目标在左半部分
		else if (this->words[middle].getEnglish() > word)
			right = middle - 1;
		//目标在右半部分
		else left = middle + 1;
	}
	return -1;
	//cout<<"查找失败!"<<endl;
	//return *(new WordNode());
}

//根据单词二分查找相应的条目,查找失败返回空
int Data::binarySearchr(string word) {
    
    
	int left = 0;
	int right = this->getDicSize() - 1;
	//进行二分查找
	while (left <= right) {
    
    
		//中间元素
		int middle = (left + right) / 2;
		//找到目标
		if (this->words[middle].getChinese() == word)
			return middle;
		//目标在左半部分
		else if (this->words[middle].getChinese() > word)
			right = middle - 1;
		//目标在右半部分
		else left = middle + 1;
	}
	return -1;
	//cout<<"查找失败!"<<endl;
	//return *(new WordNode());
}



//重新对单词按字典序进行排序
void Data::sortWords() {
    
    
	sort(this->words.begin(), this->words.end(), myless());


	//对words中的从words.begin()(包括它)到words.end()(不包括它)的元素进行从小到大排列
	//sort(words.begin(), words.end());

	//sort()排序函数是C++头文件include<algorithm>中的函数
	//sort(首元素地址(必填), 尾元素地址的下一个地址(必填), 比较函数(非必填));

	return;
}

MyFrame.h

#pragma once
#ifndef MYFRAME_H
#define MYFRAME_H

#include<iostream>
#include<string>
#include<vector>
#include<iomanip>
#include "data.h"
#include "WordS.h"

using namespace std;

//与前台显示有关的功能在该类中声明并实现
class MyFrame {
    
    
	//fstream file;
	//fstream file1;
	
public:
	//空构造函数
	MyFrame();
	//初始化整个界面
	void initialize();
	//添加新单词
	void addWord();
	//删除已有的单词
	void deleteWord();
	//修改现有的单词
	void modifyWord();
	//浏览词典
	void browseDic();
	//退出系统
	void exitSystem();
	//模拟考试
	void exam();
	//背单词练习
	void exercise();
	//控制格式,输出一些空行
	void konghang(int n);
private:
	//相关的内容数据库
	Data dataBase;
	
};

#endif

MyFrame.cpp

#include<iostream>
#include<string>
#include<vector>
#include<ctime>
#include<cstring>
#include<iomanip>
#include<time.h>
#include "MyFrame.h"

using namespace std;


//空构造函数
MyFrame::MyFrame() {
    
    
	
}

//初始化整个界面
void MyFrame::initialize() {
    
    
	int order;
	bool valid = true;
	system("title 背单词小程序控制台");

	//循环执行
	while (1) {
    
    
		system("cls");
		this->konghang(5);//打印空行
		cout << "******************************** 背单词程序 ************************************" << endl;
		this->konghang(5);
		cout << "                                1.添加单词                                     " << endl;
		cout << "                                2.删除单词                                     " << endl;
		cout << "                                3.修改单词                                     " << endl;
		cout << "                                4.顺序测验                                     " << endl;
		cout << "                                5.随机测验                                     " << endl;
		cout << "                                6.查看词典                                     " << endl;
		cout << "                                7.退出系统                                     " << endl;
		this->konghang(2);
		//判断上次输入是否有效
		if (valid == true)
			cout << "请输入您的选择:";
		else cout << "输入无效,请重新输入:";

		cin >> order;
		//判断输入是否有效
		if ((order > 0) && (order < 8))
			valid = true;
		//无效则需要重新输入
		else {
    
    
			valid = false;
			system("cls");
			continue;
		}

		//根据用户输入选择不同功能
		if (order == 1)
			this->addWord();
		else if (order == 2)
			this->deleteWord();
		else if (order == 3)
			this->modifyWord();
		else if (order == 4)
			this->exercise();
		else if (order == 5)
			this->exam();
		else if (order == 6)
			this->browseDic();
		else if (order == 7)
			this->exitSystem();
	}
}

//添加新单词
void MyFrame::addWord() {
    
    
	string english;
	string chinese;
	system("cls");
	this->konghang(3);//空格

	cout << "请输入待添加的新单词:";
	cin >> english;
	//查找该单词是否已经存在
	if (this->dataBase.binarySearch(english) >= 0) {
    
    //dataBase 私有 头文件中 相关内容的数据库 
		//binarySearch二分法查找
		//this指向Myframe中的私有数据
		cout << "单词" << english << "已经存在!" << endl;
		cout << "添加失败" << endl;
		system("pause");
		return;
	}
	cout << "请输入单词的中文解释:";
	cin >> chinese;
	WordS wordNode(english, chinese);
	this->dataBase.addWordNode(wordNode);
	cout << "成功添加单词!" << endl;
	//重新对单词进行排序
	//this->dataBase.sortWords();
	system("pause");
	return;
}

//删除已有的单词
void MyFrame::deleteWord() {
    
    
	string english;
	string chinese;
	system("cls");
	this->konghang(3);
	cout << "请输入要删除的单词:";
	cin >> english;
	//查找相应的单词是否存在
	int number = this->dataBase.binarySearch(english);
	//单词不存在
	if (number < 0) {
    
    
		cout << "单词" << english << "不存在!" << endl;
		cout << "删除失败!" << endl;
		system("pause");
		return;
	}
	//查找到了相应单词
	else {
    
    
		this->dataBase.deleteWordNode(number);
		cout << "成功删除单词!" << endl;
		system("pause");
		return;
	}
	return;
}

//修改现有的单词
void MyFrame::modifyWord() {
    
    
	string english;
	string chinese;
	system("cls");
	this->konghang(3);
	cout << "请输入要修改的单词:";
	cin >> english;
	//二分查找相应的单词
	int number = this->dataBase.binarySearch(english);
	//未找到输入的单词
	if (number < 0) {
    
    
		cout << "单词" << english << "不存在!" << endl;
		cout << "修改单词失败!" << endl;
		system("pause");
		return;
	}
	//找到单词
	else {
    
    
		cout << "请输入新的解释:";
		cin >> chinese;
		WordS wordNode(english, chinese);
		//修改相应的单词
		this->dataBase.setWordNode(number, wordNode);
		cout << "成功修改单词!" << endl;
		system("pause");
		return;
	}
	return;
}

//浏览词典
void MyFrame::browseDic() {
    
    
	int order;
	system("cls");
	this->konghang(5);
	//输出提示信息
	cout << "********************************请选择查看方式*********************************" << endl;
	this->konghang(5);
	cout << "                                1.英译汉                                     " << endl;
	cout << "                                2.汉译英                                     " << endl;
	cout << "                                3.浏览全部                                   " << endl;
	cout << "                                4.查看未背过的单词                           " << endl;
	  
	this->konghang(3);
	cout << "请选择浏览方式:";
	cin >> order;
	//检查输入的有效性
	if (!(order >= 1 && order <= 4)) {
    
    
		cout << "输入无效!" << endl;
		system("pause");
		return;
	}

	//分不同输入提供不同功能
	if (order == 1) {
    
    
		system("cls");
		cout << "请输入要查询的单词:";
		string english;
		int number;
		cin >> english;
		//未找到相应单词
		number = this->dataBase.binarySearch(english);
		if (number < 0) {
    
    
			cout << "找不到单词" << english << endl;
			system("pause");
			return;
		}
		//找到了相应的单词
		else {
    
    
			cout << endl;
			cout << "释义:" << this->dataBase.getWordNode(number).getChinese() << endl;
			system("pause");
			return;
		}
	}
	//浏览部分单词

	else if (order == 2) {
    
    
		system("cls");
		cout << "请输入要查询的汉语:";
		string chinese;
		int number;
		cin >> chinese;
		//未找到相应单词
		number = this->dataBase.binarySearchr(chinese);
		if (number < 0) {
    
    
			cout << "找不到单词" << chinese << endl;
			system("pause");
			return;
		}
		//找到了相应的单词
		else {
    
    
			cout << endl;
			cout << "释义:" << this->dataBase.getWordNode(number).getEnglish() << endl;
			system("pause");
			return;
		}
	}

	
	
	
	//浏览全部单词
	else if (order == 3) {
    
    

		//fstream iofile;//可读可写 
		//iofile.iofile("dicA.txt", ios::in | ios::out);//以输入和输出的方式打开文件,文件可读可写 
		//while (!iofile.eof()) {
    
    
          int length = this->dataBase.getDicSize();
		  for (int i = 0; i < length; i++) {
    
    
			cout << this->dataBase.getWordNode(i).getEnglish()
				<< "   " << this->dataBase.getWordNode(i).getChinese() << endl;
		}

		
		//}
		//iofile.close();
		system("pause");

		return;
	}
	//浏览部分单词
	else if (order == 4) {
    
    
		int begin;
		int length;
		cout << "请输入记过的单词个数:";
		cin >> begin;
		cout << "请输入要查看的单词个数:";
		cin >> length;
		if ((begin < 0) || (length <= 0) || (begin + length) > this->dataBase.getDicSize()) {
    
    
			cout << "输入无效!无法满足查看要求!" << endl;
			system("pause");
			return;
		}
		system("cls");
		for (int i = 0; i < length; i++) {
    
    
			cout << this->dataBase.getWordNode(begin + i).getEnglish()
				<< "   " << this->dataBase.getWordNode(begin + i).getChinese() << endl;
		}
		system("pause");
		return;
	}

	return;
}

//退出系统
void MyFrame::exitSystem() {
    
    
	exit(0);
}

//模拟考试
void MyFrame::exam() {
    
    
	system("cls");
	this->konghang(3);
	cout << "********************************考试说明************************************" << endl;
	this->konghang(3);
	cout << "  本考试共有n题,全部为选择题,每题5分。选择你认为正确的答案并输入,回车确认之后即可进入下一题"
		<< endl;
	cout << "                                Good Luck!                                  " << endl;
	this->konghang(10);
	system("pause");
	//system("cls");

	//存放单词所对应的编号



	int number[5];
	//记录得分
	int score;
	srand(unsigned(time(0)));//防止随机数的每次重复
	//随机选取n个单词
	for (int i = 0; i < 5; i++) {
    
    //修改练习题目
		int tmp = rand() % (this->dataBase.getDicSize());
		bool repeat = false;
		//检查随机生成的单词是否重复
		for (int j = 0; j < i; j++) {
    
    
			if (number[j] == tmp)
				repeat = true;
		}
		if (repeat == true)
			i--;
		else
			number[i] = tmp;
	}

	score = 0;
	//开始考试
	for (int i = 0; i < 5; i++) {
    
    //修改练习题目
		system("cls");
		this->konghang(1);
		int meaning[4];
		//随机生成错误答案
		for (int j = 0; j < 4; j++) {
    
    
			int tmp = rand() % (this->dataBase.getDicSize());
			bool repeat = false;
			//检查是否有重复的情况
			for (int k = 0; k < j; k++) {
    
    
				if (meaning[k] == tmp)
					repeat = true;
			}
			if ((repeat == true) || (tmp == number[i]))
				j--;
			else
				meaning[j] = tmp;
		}
		//生成正确答案
		int ans = rand() % 4;
		meaning[ans] = number[i];
		cout << "第" << (i + 1) << "题:" << endl << endl;
		cout << "单词" << this->dataBase.getWordNode(number[i]).getEnglish()
			<< "的意思与下列那个选项符合?" << endl;
		this->konghang(2);
		for (int j = 0; j < 4; j++) {
    
    
			cout << (char)(j + 'A') << "  " << this->dataBase.getWordNode(meaning[j]).getChinese()
				<< endl;
		}
		this->konghang(8);
		cout << "请输入你的答案:";
		while (1) {
    
    
			string yourAns;
			cin >> yourAns;
			//输入的答案无效
			if ((yourAns.size() != 1) ||
				(yourAns != "A" && yourAns != "B"
					&& yourAns != "C" && yourAns != "D")) {
    
    
				cout << "输入无效,请重新输入:";
				continue;
			}
			else {
    
    
				//答案正确
				if (yourAns[0] == (ans + 'A'))
					score += 5;
				break;
			}
		}//结束while循环
	}// 结束for循环

	system("cls");
	
	this->konghang(3);
	if (score < 5 * 5 * 0.6) {
    
    
		cout << "您只得了" << score << "分><, 尚需努力!" << endl;
	}
	else if ((score >= 5 * 5 * 0.6) && (score < 5 * 5 * 0.8)) {
    
    
		cout << "您得了" << score << "分, 及格颇有余,优秀尚不足,加油!" << endl;
	}
	else if ((score >= 5 * 5 * 0.8) && (score < 5 * 5)) {
    
    
		cout << "您得了" << score << "分, 非常优秀,加油!" << endl;
	}
	else if (score == 5 * 5) {
    
    
		cout << "您得了满分! 膜拜神牛!" << endl;
	}
	cout << endl << "下面是所有正确答案:" << endl;

	for (int i = 0; i < 5; i++) {
    
    //修改练习题目
		cout << i + 1 << ": " << this->dataBase.getWordNode(number[i]).getEnglish()
			<< "   " << this->dataBase.getWordNode(number[i]).getChinese() << endl;
	}
	system("pause");
	return;
}




//顺序测验
void MyFrame::exercise() {
    
    
	srand(unsigned(time(0)));
	//int a;
	while (1) {
    
    

		char cmd[100];
		system("cls");
		this->konghang(5);
		int number = this->dataBase.getDicSize();
		
		for (int i = 0; i < number; i++) {
    
    
			
		cout << "单词:   " << this->dataBase.getWordNode(i).getEnglish() << endl;
		cout << endl;
		cout << "解释:   " << this->dataBase.getWordNode(i).getChinese() << endl;
		
		cout << "按照单词表顺序,你已经记住的单词数:" << i+1 <<endl;
		this->konghang(5);
		cout << "输入quit退出练习,按回车键可查看下一个单词";
		cin.getline(cmd, 100);
		//退出背单词的模式
		if (strcmp(cmd, "quit") == 0) {
    
    
			
			system("cls");
			return;
		}
		else
			continue;
		
		}

	}
	
	return;
}

//控制格式,输出一些空行
void MyFrame::konghang(int n) {
    
    
	//输出空行
	for (int i = 0; i < n; i++)
		cout << endl;
	return;
}

myless.cpp

#include<iostream>
#include<string>
#include "WordS.h"

using namespace std;

//用来辅助排序
class myless
{
    
    
public:
	//重载运算符,为排序做准备
	bool operator()(WordS a, WordS b) {
    
    
	
		//按照字典序排列  按照字母顺序
		return a.getEnglish() < b.getEnglish();

		//return 1 > 2; 返回false
	}
};

main.cpp

/*
*
*Author: lwfcgz
*Student ID: it is secret, haha~~
*Date: 2012-12-27
*Environment: Microsoft Visual Studio 2008
*
*/

#include<iostream>
#include<string>
#include<stdlib.h>
#include<stdio.h>
#include<vector>
#include<graphics.h>//包含图形库头文件,
#include "data.h"
#include "WordS.h"
#include "MyFrame.h"

using namespace std;

//主函数
int main() {
    
    

	system("color F5");
	
	initgraph(640,450,SHOWCONSOLE);//创建一个窗口 显示控制台

	setbkcolor(BROWN);//背景颜色为棕色1
	cleardevice();//清屏2

	//参数错误,找不到对应的函数,error C2665: “settextstyle”: 4 个重载中没有一个可以转换所有参数类型
	//是由于字符集导致食物,解决方案
	//1.在字符串前面加上大写的L
	//2.用TEXT()把字符串包起来,_T(),原理同上
	//3.不需要添加任何代码,进项目->属性->配置属性->常规->字符集->改为多字节字符集,推荐使用
	
	settextstyle(50,0,L"楷体");//设置文字样式,大小,字体
	setbkmode(TRANSPARENT);//设置背景模式trans parent透明
	settextcolor(RGB(34,633,82));
	outtextxy(50, 50, L"欢迎使用背单词小程序");

	MyFrame frame;//生成一个界面对象
	frame.initialize();//初始化界面并将控制转移到该对象内部
	getchar();//使窗口停留
	closegraph();//关闭窗口
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_53524766/article/details/125155716