文本查询编程实验
文本查詢編程實(shí)驗(yàn)
- 設(shè)計(jì)思路
- 文本查詢程序
- 數(shù)據(jù)結(jié)構(gòu)
- 代碼細(xì)節(jié)
- 源代碼
- TextQuery.h
- TextQuery.cpp
- QueryResult.h
- make_plural.h
- querymain.cpp
本文是本人大一期間在校學(xué)習(xí)C++課程時(shí)所撰寫的實(shí)驗(yàn)報(bào)告的摘錄,由于剛上大學(xué),剛接觸計(jì)算機(jī)編程方面的相關(guān)知識(shí),故可能會(huì)有很多不足甚至錯(cuò)誤的地方,還請(qǐng)各位看官指出及糾正。
本文所涉及到的“教材”指:
電子工業(yè)出版社《C++ Primary中文版(第5版)》
如需轉(zhuǎn)載或引用請(qǐng)標(biāo)明出處。
設(shè)計(jì)思路
文本查詢程序
該程序要求用戶在一個(gè)給定的文本中輸入要查詢的單詞,查詢的結(jié)果是該單詞在文本中出現(xiàn)的次數(shù)及其所在的行的列表。根據(jù)這個(gè)要求,可以預(yù)見到該程序需要做到:
- 當(dāng)程序讀入文件時(shí),要記住單詞出現(xiàn)的每一行。因此,程序需要逐行讀取輸入的文件,并且將每一行分解為獨(dú)立的單詞;
- 輸出結(jié)果時(shí),要能提取每個(gè)單詞所關(guān)聯(lián)的行號(hào),且行號(hào)必須按一定順序出現(xiàn)且無(wú)重復(fù),還要能夠打印給定行號(hào)中的文本。
數(shù)據(jù)結(jié)構(gòu)
使用之前學(xué)過(guò)的各種標(biāo)準(zhǔn)庫(kù)類型可以簡(jiǎn)單且方便地實(shí)現(xiàn)上述要求:
- 使用一個(gè)存儲(chǔ)string地vector用于保存輸入文本地拷貝,其中的每一個(gè)元素是輸入文本對(duì)應(yīng)的每一行;
- 使用一個(gè)istringstream,將每一行切分為一個(gè)個(gè)的單詞;
- 使用一個(gè)set來(lái)保存每個(gè)單詞在文本中出現(xiàn)的行號(hào),從而保證了輸出的規(guī)范性;
- 使用一個(gè)map來(lái)講每個(gè)單詞與它出現(xiàn)行號(hào)的集合set關(guān)聯(lián)起來(lái),以方便地提取任意單詞的行號(hào)集合。
上述的vector、set和map使用智能共享指針shared_ptr來(lái)進(jìn)行相關(guān)操作。
此外,定義一個(gè)TextQuery類用于執(zhí)行讀入文本、查詢等的相關(guān)操作,還定義一個(gè)QueryResult類用于表示查詢的結(jié)果。這兩個(gè)類有助于模塊化代碼,使整個(gè)解決方案更加有效。
代碼細(xì)節(jié)
總的來(lái)說(shuō),整個(gè)程序的運(yùn)行分成以下五步:
其它地方還實(shí)現(xiàn)了規(guī)范化文本、新定義begin、end函數(shù)等操作,具體細(xì)節(jié)詳見代碼注釋部分。
源代碼
TextQuery.h
#ifndef TEXTQUERY_H #define TEXTQUERY_H #include <memory> #include <string> #include <vector> #include <map> //使用map #include <set> //使用set #include <fstream> #include "QueryResult.h"class QueryResult; //因?yàn)門extQuery有成員函數(shù)返回QueryResult,故需事先聲明 class TextQuery { public:typedef std::vector<std::string>::size_type line_no; //line_no是的vector的size_type,用于指示行號(hào)TextQuery(std::ifstream&); //TextQuery的構(gòu)造函數(shù)QueryResult query(const std::string&) const; //執(zhí)行查詢操作void display_map(); //用于打印查找映射的文本的調(diào)試函數(shù) private:std::shared_ptr<std::vector<std::string>> file; //file指向存儲(chǔ)文本每一行的vectorstd::map<std::string, std::shared_ptr<std::set<line_no>>> wm; //將每個(gè)要查詢的單詞映射到存儲(chǔ)其出現(xiàn)行號(hào)的集合static std::string cleanup_str(const std::string&); //將文本中的標(biāo)點(diǎn)符號(hào)刪去,并小寫所有字母 }; #endifTextQuery.cpp
#include "TextQuery.h" #include "make_plural.h" //使用復(fù)數(shù)形式輸出函數(shù)#include <cstddef> #include <memory> #include <sstream> //使用istringstream,將行切分為單詞 #include <string> #include <vector> #include <map> //使用map #include <set> //使用set #include <iostream> #include <fstream> #include <cctype> //使用字符識(shí)別函數(shù) #include <cstring> #include <utility>using namespace std;//下面使用typedef簡(jiǎn)化代碼 //wmType是將查詢單詞映射到出現(xiàn)行號(hào)的map typedef map<string, shared_ptr<set<TextQuery::line_no>>> wmType; //wmIter是wmType的常量迭代器 typedef wmType::const_iterator wmIter; //lineType是指向存儲(chǔ)行號(hào)的set的共享指針 typedef shared_ptr<set<TextQuery::line_no>> lineType; //lineIter是存儲(chǔ)行號(hào)的set的常量迭代器 typedef set<TextQuery::line_no>::const_iterator lineIter;//構(gòu)造函數(shù):讀取文本并建立起每一種單詞映射到其出現(xiàn)的行號(hào)的集合map TextQuery::TextQuery(ifstream &is): file(new vector<string>) {string text; //指示當(dāng)前行while (getline(is, text)) //對(duì)于文本中的每一行{file->push_back(text); //將該行存儲(chǔ)到file中int n = file->size() - 1; //n為當(dāng)前這一行的行號(hào)istringstream line(text); //string讀入流line,用于將當(dāng)前行劃分為單詞string word; //指示當(dāng)前單詞 while (line >> word) //對(duì)于當(dāng)前行的每一個(gè)單詞{word = cleanup_str(word); //規(guī)范單詞//如果該單詞是第一次出現(xiàn),則會(huì)在wm新建一個(gè)該單詞映射到行號(hào)的鍵值對(duì)lineType &lines = wm[word]; //lines指向一個(gè)存儲(chǔ)行號(hào)的setif (!lines) //當(dāng)?shù)谝淮斡鲆妴卧~時(shí),lines指向NULLlines.reset(new set<line_no>); //因此分配一個(gè)新的set用于存儲(chǔ)該單詞出現(xiàn)的行號(hào)lines->insert(n); //將出現(xiàn)的行號(hào)放入到lines中}} } //移除標(biāo)點(diǎn)符號(hào)并將字母小寫化,以便查詢可以無(wú)視大小寫 string TextQuery::cleanup_str(const string &word) {string ret;for (string::const_iterator it = word.begin(); it != word.end(); ++it){if (!ispunct(*it)) //如果不是標(biāo)點(diǎn)符號(hào)ret += tolower(*it); //則小寫化該字符}return ret; //返回處理后的單詞 }QueryResult TextQuery::query(const string &sought) const {static lineType nodata(new set<line_no>); //nodata是指向空行號(hào)集的lineType,在找不到單詞時(shí)返回wmIter loc = wm.find(cleanup_str(sought)); //loc接受map成員函數(shù)find的返回值if (loc == wm.end()) //如果沒(méi)找到return QueryResult(sought, nodata, file); //則用nodata構(gòu)造一個(gè)查詢結(jié)果QueryResult作為返回值else return QueryResult(sought, loc->second, file); //否則返回用對(duì)應(yīng)行號(hào)集合set構(gòu)造的查詢結(jié)果QueryResult }ostream &print(ostream &os, const QueryResult &qr) {//如果單詞找到了,則輸出出現(xiàn)次數(shù)和所有出現(xiàn)的位置os << qr.sought << " occurs " << qr.lines->size() << " "<< make_plural(qr.lines->size(), "time", "s") << endl;//輸出該單詞出現(xiàn)的每一行for (lineIter num = qr.lines->begin(); num != qr.lines->end(); ++num) //編號(hào)從0開始,因此輸出要加1os << "\t(line " << *num + 1 << ") " << *(qr.file->begin() + *num) << endl;return os; }//用于調(diào)試代碼的函數(shù) void TextQuery::display_map() {wmIter iter = wm.begin(), iter_end = wm.end();//對(duì)于map中的每個(gè)單詞for ( ; iter != iter_end; ++iter) {cout << "word: " << iter->first << " {";//獲取位置向量作為常量引用以避免復(fù)制它lineType text_locs = iter->second;lineIter loc_iter = text_locs->begin(),loc_iter_end = text_locs->end();//打印此單詞的所有行號(hào)while (loc_iter != loc_iter_end){cout << *loc_iter;if (++loc_iter != loc_iter_end)cout << ", ";}cout << "}\n"; //結(jié)束輸出}cout << endl;//結(jié)束打印整個(gè)map }QueryResult.h
#ifndef QUERYRESULT_H #define QUERYRESULT_H #include <memory> #include <string> #include <vector> #include <set> //使用set #include <iostream>//QueryResult類表示查詢結(jié)果 class QueryResult { friend std::ostream& print(std::ostream&, const QueryResult&); //聲明友元函數(shù)print public:typedef std::vector<std::string>::size_type line_no; //line_no是的vector的size_type,用于指示行號(hào)typedef std::set<line_no>::const_iterator line_it; //set存儲(chǔ)行號(hào),line_it是它的迭代器QueryResult(std::string s, //構(gòu)造查詢結(jié)果類std::shared_ptr<std::set<line_no> > p, std::shared_ptr<std::vector<std::string> > f):sought(s), lines(p), file(f) { }std::set<line_no>::size_type size() const { return lines->size(); } //返回單詞出現(xiàn)的次數(shù)line_it begin() const { return lines->begin(); } //返回行號(hào)集合的開頭迭代器 line_it end() const { return lines->end(); } //返回行號(hào)集合的尾后迭代器std::shared_ptr<std::vector<std::string>> get_file() { return file; } private:std::string sought; //指示本次查詢結(jié)果對(duì)應(yīng)的單詞std::shared_ptr<std::set<line_no>> lines; //lines指向存儲(chǔ)行號(hào)的集合setstd::shared_ptr<std::vector<std::string>> file; //file是指向存儲(chǔ)文本的vector };std::ostream &print(std::ostream&, const QueryResult&); //打印查詢結(jié)果 #endifmake_plural.h
#include <cstddef> #include <string> #include <iostream>using namespace std;#ifndef MAKE_PLURAL_H #define MAKE_PLURAL_H//輸出時(shí)如果ctr大于1,則輸出word對(duì)應(yīng)的復(fù)數(shù)形式 inline string make_plural(size_t ctr, const string &word, const string &ending) {return (ctr > 1) ? word + ending : word; }#endifquerymain.cpp
#include <string> #include <fstream> #include <iostream>using namespace std;#include <cstdlib> //使用EXIT_FAILURE #include "TextQuery.h" #include "make_plural.h"void runQueries(ifstream &infile) {//infile是我們想要查詢的文本的文件輸入流 TextQuery tq(infile);//存儲(chǔ)文本,并構(gòu)造其中每個(gè)單詞到其出現(xiàn)位置集合的映射 //提示用戶輸入,并輸出查詢結(jié)果while (true){cout << "enter word to look for, or q to quit: ";string s;if (!(cin >> s) || s == "q") break; //當(dāng)輸入空行或“q”時(shí)關(guān)閉程序print(cout, tq.query(s)) << endl; //輸出查詢結(jié)果} }//該程序接受一個(gè)參數(shù),此參數(shù)指定要查詢的文件 int main(int argc, char **argv) {//infile是我們想要查詢的文本的文件輸入流ifstream infile;//由于open函數(shù)無(wú)返回值,因此我們使用逗號(hào)運(yùn)算符去查詢infile調(diào)用open后的狀態(tài)if (argc < 2 || !(infile.open(argv[1]), infile)){ //如果文件打開失敗,將錯(cuò)誤輸出到標(biāo)準(zhǔn)錯(cuò)誤流cerr << "No input file!" << endl;return EXIT_FAILURE; } runQueries(infile); //打開成功,進(jìn)行查詢system("pause");return 0; }總結(jié)
- 上一篇: allocator类编程实验
- 下一篇: 类模板编程实验