需求
程序读取用户指定的任意文本文件,允许用户从该文件中查找单词。查询结果是该单词出现的次数,并列出每次出现所在行,如果某单词在同一行中多次出现,程序将只显示该行一次。行号按升序显示,即第 7 行应该在第 9 行之前输出,依此类推。例如,以本章的内容作为文件输入,然后查找单词“element”。输出的前几行应为:
element occurs 125 times
(line 62) element with a given key.
(line 64) second element with the same key.
(line 153) element |==| operator.
(line 250) the element type.
(line 398) corresponding element.
后面省略了大约 120 行。
看着书上的例子,自己写了下,大致思路是
读取文件,将文件以行为单位,放入vector<string>,再遍历vector<string>,将每行每个单词读入map< string,set<unsigned> >中,最后从map中查找读取
注意的地方
程序是不区分大小写的,还需要去掉文章中的标点需要调用函数
头文件#include<cctype>
ispunct() 检查是否是非空格、非数字和非英文字母,类似函数isspace,isdigit,isalpha
tolower() 把字符转换成小写字母
对const成员的迭代器需要用const_iterator
对于内置类型,和长度比较短(8字节以内)的浅层结构,类等对象,传值比传引用效率更高。
对超过8字节的对象,一般传引用效率更高。
但实际上传引用或传值的选择,主要取决于功能需求而非效率需求。
详解
获取文件对象:
1 ifstream& open_file(ifstream &in,const string &file) 2 { 3 in.close(); 4 in.clear(); 5 in.open(file.c_str()); 6 return in; 7 }
读文件,创建vector和map
1 void TextQuery::read_file(ifstream &in) 2 { 3 store_file(in); 4 build_map(); 5 } 6 7 void TextQuery::store_file(ifstream &in) 8 { 9 string textline; 10 while(getline(in,textline)) 11 lines_of_text.push_back(textline); 12 } 13 14 void TextQuery::build_map() 15 { 16 for(line_no line_num = 0;line_num != lines_of_text.size();line_num++) 17 { 18 istringstream line(lines_of_text[line_num]); 19 string word; 20 while(line >> word) 21 { 22 word = cleanup_str(word); 23 word_map[word].insert(line_num); 24 } 25 26 } 27 }
处理单词中的符号,忽略大小写
1 string TextQuery::cleanup_str(const string &word) 2 { 3 string ret; 4 for(string::const_iterator it = word.begin();it != word.end();++it) 5 { 6 if(!ispunct(*it)) 7 ret += tolower(*it); 8 } 9 return ret; 10 }
查找单词,返回值是map的第二个元素set,用来保存单词所在的行号
1 set<TextQuery::line_no> TextQuery::run_query(const string &query_word) const 2 { 3 map< string,set<line_no> >::const_iterator loc = word_map.find(query_word); 4 if( loc == word_map.end() ) 5 return set<line_no>(); //找不到返回空的set对象 6 else 7 return loc ->second; 8 }
返回查找结果,这里有个漏洞,查找单词的数目是为单词所出现的行数(因为set不存储重复元素)
void TextQuery::print_result(set<line_no> locs,const string &query_word) { cout<<query_word<<":"<<locs.size()<<endl; set<line_no>::iterator it = locs.begin(); while(it != locs.end()) { cout<<"(line "<<*it+1<<") "; cout<<lines_of_text[*it]<<endl; it++; } }
写了两个辅助函数,并未调用,为了查看vector和map中的内容
1 void TextQuery::show_vec() 2 { 3 vector<string>::iterator ite = lines_of_text.begin(); 4 while(ite != lines_of_text.end()) 5 cout<<*ite++<<endl; 6 } 7 void TextQuery::show_map() 8 { 9 map< string,set<line_no> >::iterator loc = word_map.begin(); 10 while(loc != word_map.end()) 11 { 12 cout<<loc->first<<"\t"; 13 set<line_no>::iterator it = (loc->second).begin(); 14 while(it != (loc->second).end()) 15 { 16 cout<<*it + 1<<" "; 17 it++; 18 } 19 cout<<endl; 20 loc++; 21 } 22 }
运行结果
代码点此下载