需求
程序讀取用戶指定的任意文本文件,允許用戶從該文件中查找單詞。查詢結果是該單詞出現的次數,并列出每次出現所在行,如果某單詞在同一行中多次出現,程序將只顯示該行一次。行號按升序顯示,即第 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 }
運行結果
?
代碼點此下載