最近在学字符串,学了如何解析数据,下面就来尝试一下用C++字符串实现英汉词典的搜索功能吧。
实现了这个,没有网络也能快速查词噢!
我这篇文章用的是一个txt文件,点击百度云链接领取文件==(提取码:C415)==,然后操作的时候我把这个txt文件和我的cpp文件放在一个文件夹里面。
(词典截图)
看到这页截图,是不是十分眼熟呢,每次背单词都从abandon开始背起,但也就熟悉a开头的那些,单词书的后面是崭新的,快要考六级了,一点也不慌(bushi)
观察上面词典的字符串形式 先是单词,然后是空格,然后是释义。查词的过程中,我们要输入的单词是一个字符串,那检索的时候就是判断字符串相等的过程。
那么用几个常用的函数就可以实现了, find()查找空格,然后substr()取出前面的单词,再与输入的单词对比
但是这个程序也有弊端,因为频繁的使用硬盘读写,效率低,损耗大。
#include
#include
#include //字符串流
#include
using namespace std;
class test
{
private:
ifstream f; //文件句柄
string s,S; //S是整个一行 s是取出的字串:单词
string word;//word是用户输入的所需查询的单词
string::size_type n;//n是第一个空格的位置
public:
test()
{
f.open("英汉词典.txt",ios::in);//in和out是针对程序而言的,要读文件,把文件读进来,读到程序里来
if(!f)
{
cout<<"file open error!"<<endl;
}
}
void dict()
{
cout<<"input a word:";
cin>>word;
while(!f.eof())
{
getline(f,S);//getline读一行,此函数可读取整行,包括前导和嵌入的空格,并将其存储在字符串对象中。
n=S.find(" ",0);
s=S.substr(0,n);
if(s==word)
{
cout<<S<<endl;
}
}
}
~test()
{
f.close();
}
};
int main()
{
test t;
t.dict();
return 0;
}
map容器非常擅长检索,用map容器实现词典功能,一次性将文件读入了容器(内存),将磁盘操作转换为内存操作,减少了损耗,且十分高效。
把单词作为map的key,把单词以及释义那一整行作为map的value。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class test
{
private:
ifstream f;
string S,word,s;
string::size_type n;
map<string,string>a;
map<string,string>::iterator p;
public:
test()
{
f.open("英汉词典.txt",ios::in);
if(!f)
{
cout<<"file open error!"<<endl;
}
else
{
while(!f.eof())
{
getline(f,S);
n=S.find(" ",0);
s=S.substr(0,n);
a.insert(pair<string,string>(s,S));
}
}
}
void dict()
{
cout<<"input a word:";
cin>>word;
p=a.find(word);
if(p!=a.end())
{
cout<<p->second<<endl;
}
else
{
cout<<"no record"<<endl;
}
}
~test()
{
f.close();
}
};
int main()
{
test t;
t.dict();
return 0;
}
把整个文件一次性读入一个字符串,精确查询的效率比方法一更高,但没有容器效率高,操作简洁,可以实现模糊查询(方法二map只能键值)
用#分隔每个词条,不要用“-”来分搁,因为有如by-product这种单词
#include
#include
#include
#include
using namespace std;
class test
{
private:
ifstream f;
string s;//#单词#
string s1;//提取单词,与输入的进行比较
string ss;//单词+释义
string word;//输入的词
string::size_type n1,n2,n3; //定位
string::size_type pos;//迭代变量
public:
test()
{
f.open("英汉词典.txt",ios::in);
if(!f)
{
cout<<"file open error!"<<endl;
exit(0);
}
else
{
s="#";
while(!f.eof())
{
getline(f,ss);
s=s+ss+"#";//#词条#词条#词条...词条#
}
}
}
void dict()
{
cout<<"input a word:";
cin>>word;
pos=0;
while(true)
{
if(s.find(word,pos)!=string::npos)
{
n1=s.find(word,pos);//word本身就是字符串了,不要加“”,
n1=s.rfind("#",n1);//可能出现查找on,但是y有的单词比如moon里面有on这种情况,所有要rfind #
n2=s.find(" ",n1);
n3=s.find("#",n1+1);
s1=s.substr(n1+1,n2-n1-1);
ss=s.substr(n1+1,n3-n1-1);
if(s1==word)
{
cout<<ss<<endl;//输出词条
break;//找到了就不用继续找了
}
pos=n3;
}
}
}
~test()
{
f.close();
}
};
int main()
{
test t;
t.dict();
return 0;
}
#include
#include
#include
#include
using namespace std;
class test
{
private:
ifstream f;
string s;//#单词#
string s1;//提取单词,与输入的进行比较
string ss;//单词+释义
string word;//输入的词
string::size_type n1,n2,n3; //定位
string::size_type pos;//迭代变量
public:
test()
{
f.open("英汉词典.txt",ios::in);
if(!f)
{
cout<<"file open error!"<<endl;
exit(0);
}
else
{
s="#";
while(!f.eof())
{
getline(f,ss);
s=s+ss+"#";//#词条#词条#词条...词条#
}
}
}
void dict()
{
cout<<"input a word:";
cin>>word;//输入英文中文都可以
pos=0;
while(true)
{
if(s.find(word,pos)!=string::npos)
{
n1=s.find(word,pos);//word本身就是字符串了,不要加“”,
n1=s.rfind("#",n1);
n3=s.find("#",n1+1);
ss=s.substr(n1+1,n3-n1-1);
//不需要找空格了,直接把含有你所输入的模糊word那一个夹在两个#之间的词条输出来
cout<<ss<<endl;
pos=n3;
}
}
}
~test()
{
f.close();
}
};
int main()
{
test t;
t.dict();
return 0;
}
方法三中用到了解析数据的方法,这个东西熟练一点就会了,以下为和我一样的初学者提供几个解析数据的小练习
①从键盘输入一个形如“-s1-s2-s3-”的字符串,将该字符串分解为三个子串s1、s2、s3,并且输出这三个子串。
(s1、s2、s3为长度不等、内容不定的字符串) 将解析出来的s1、s2、s3…子串压入容器,通过容器输出其中的所有元素。
#include
#include
#include
using namespace std;
class test
{
private:
string s;
vector<string>a;
vector<string>::iterator p;
public:
test()
{
s="-da21g-xchyyy8-dsauuo09765-";//随便打字的
}
void input()
{
string::size_type pos,n;
pos=0;//迭代变量
while(true)
{
if(s.find("-",pos)!=string::npos)
{
pos=s.find("-",pos);
++pos;
n=pos;
if(s.find("-",pos)!=string::npos)
{
pos=s.find("-",pos);
a.push_back(s.substr(n,pos-n));
}
else
break;
}
else
break;
}
}
void browse()
{
for(p=a.begin();p!=a.end();++p)
{
cout<<*p<<endl;
}
}
};
int main()
{
test s;
s.input();
s.browse();
return 0;
}
void input()那里也可以简化一点
void input()
{
string::size_type pos,n;
pos=0;
while(true)
{
if(s.find("-",pos)!=string::npos)
{
n=s.find("-",pos);
pos=s.find("-",n+1);
a.push_back(s.substr(n+1,pos-n-1));
}
else
break;
}
}
②建立文本文件“004.txt”,里面内容为“-Zhang-86-Li-92-Wang-75”
建立类(test),类内建立vector容器,容器元素类型为结构体(S),S中有两项数据“name”、“score”,都是字符串类型。
在类中建立一个构造函数,将“004.txt”的内容解析后压入容器
output函数,使用容器输出所有学生数据。
Sort函数,按成绩降序对学生数据进行排序。
在主函数中建立一个对象,依次运行Sort、output函数。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
struct S
{
string name;
string score;
};
class test
{
private:
ifstream f;
vector<S>a;
vector<S>::iterator p;
string::size_type pos,n;
string ss;//读取整个字符串
string s1;//读取名字
string s2;//读取分数
S t;
public:
test()
{
f.open("004.txt",ios::in);
getline(f,ss);
pos=0;
while(true)
{
if(ss.find("-",pos+1)!=string::npos)
{
n=ss.find("-",pos);
pos=ss.find("-",n+1);
s1=ss.substr(n+1,pos-n-1);
t.name=s1;
n=ss.find("-",pos+1);
s2=ss.substr(pos+1,n-pos-1);
t.score=s2;
a.push_back(t);
pos=n;
}
else break;//没有break退不出来
}
}
static bool cmp(S m1,S m2)
{
return m1.score>m2.score;
}
void Sort()
{
sort(a.begin(),a.end(),cmp);
}
void output()
{
for(p=a.begin();p!=a.end();++p)
{
cout<<p->name<<"——"<<p->score<<endl;
}
}
~test()
{
f.close();
}
};
int main()
{
test t;
t.Sort();
t.output();
return 0;
}
我遇到的bug:
一是一开始忘了break
二是我原本是ss.find("-",pos),没有用pos+1,而pos最后赋值为n-1
结果解析完那些数据之外多输出了一个"–"是因为最后一次if判断仍然能进行,而找到最后一个“-”之后,后面已经取不出子串了
于是调整,最后一次if判断直接从最后一个"-"后面一个位置开始找,肯定找不到“-“了,于是就else break了
while(true)
{
if(ss.find("-",pos+1)!=string::npos)
{
n=ss.find("-",pos);
pos=ss.find("-",n+1);
s1=ss.substr(n+1,pos-n-1);
t.name=s1;
n=ss.find("-",pos+1);
s2=ss.substr(pos+1,n-pos-1);
t.score=s2;
a.push_back(t);
pos=n;
}
else break;//没有break退不出来
}
但我也产生了一个疑问,上面这段代码在最后字符串结束了还在取子串,为啥不报错呢?
我先用个小程序试验了下,取不到东西果真没报错,但前面还是把空的东西压入了容器,在遍历的时候输出了"–"
**小tips:**原先我在遍历那里是cout
所以还是用n+1干脆!
注意:因为给的几个分数位数相同所以可以直接字符串ascll码
比较,如果有92分还有101分比较,就要转化成数字比较了,不然排序排出来92比101大
另外,构造函数那里解析数据还可以清晰一点(感觉变量适当多设一点,更清晰)
#include
#include
#include
#include
#include
using namespace std;
struct S
{
string name;
string score;
};
class test
{
private:
string s;
string::size_type n1,n2,n3,pos;
vector<S>a;
vector<S>::iterator p;
ifstream f;
S t;
public:
test()
{
f.open("004.txt",ios::in);
getline(f,s);
f.close();
pos=0;
while(true)
{
if(s.find("-",pos+1)!=string::npos)//==string::npos表示没找到 !=就是找到了
{
pos=s.find("-",pos);
n1=pos;
n2=s.find("-",n1+1);
n3=s.find("-",n2+1);
t.name=s.substr(n1+1,n2-n1-1);
t.score=s.substr(n2+1,n3-n2-1);
a.push_back(t);
pos=n3;
}
else break;
}
}
static bool cmp(S s1,S s2)
{
return s1.score>s2.score;
}
void browse()
{
for(p=a.begin();p!=a.end();++p)
{
cout<<p->name<<"-"<<p->score<<endl;
}
}
void Sort()
{
sort(a.begin(),a.end(),cmp);
}
};
int main()
{
test t;
t.Sort();
t.browse();
system("pause");
return 0;
}
③学生成绩检索:
每名同学有三项数据:学号、姓名、成绩,放在一个字符串中。以三名同学的数据为例,串的内容为:
“-001-Wang-95-002-Li-98-003-Liu-98-”
其中001、002、003代表学号。
要求,从键盘输入某同学的学号,通过对字符串的处理,分割出该同学的三项数据并且分行输出。然后将这三项数据用分隔符“-”连接在一起写入文本文件“003.txt”中。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class test
{
private:
ofstream f;
string S="-001-Wang-95-002-Li-98-003-Liu-98-";
string num;//学号
string name;//姓名
string score;//分数
string ss;//三项数据连在一起
string::size_type n1,n2,n3,n4;
string seek;//用户要查找的
public:
test()
{
cout<<"input num:";
cin>>seek;
n1=S.find(seek,0);
cout<<seek<<endl;
n2=S.find("-",n1);
n3=S.find("-",n2+1);
name=S.substr(n2+1,n3-n2-1);
cout<<name<<endl;
n4=S.find("-",n3+1);
score=S.substr(n3+1,n4-n3-1);
cout<<score<<endl;
ss="-"+seek+"-"+name+"-"+score+"-";
ofstream f("003.txt",ios::out);
if(f)
{
f<<ss<<endl;
f.close();
}
}
};
int main()
{
test t;
return 0;
}
当然也可以用前面英汉词典用到的那个rfind
#include
#include
#include
#include
using namespace std;
class test
{
private:
string s,ss,s1,s2,s3;
ofstream f;
public:
test()
{
s="-001-Wang-95-002-Li-98-002-Liu-99-";
f.open("003.txt",ios::out);
}
void input()
{
cout<<"input a string:";
cin>>ss;
}
void output()
{
string::size_type n1,n2,n3,n4;
if(s.find(ss,0)!=string::npos)
{
n1=s.find(ss,0);
n1=s.rfind("-",n1);//"-"
n2=s.find("-",n1+1);//"-"
n3=s.find("-",n2+1);//"-"
n4=s.find("-",n3+1);//"-"
}
s1=s.substr(n1+1,n2-n1-1);
s2=s.substr(n2+1,n3-n2-1);
s3=s.substr(n3+1,n4-n3-1);
cout<<s1<<endl;
cout<<s2<<endl;
cout<<s3<<endl;
f<<s1+"-"+s2+"-"+s3<<endl;
}
~test()
{
f.close();
}
};
int main()
{
test t;
t.input();
t.output();
return 0;
}