博主刚开始学习c++,前段时间老师安排了c++的一个作业:

给定两个文件(一个源文件text4search.txt,一个文件keywords.txt蕴含须要在源文件中搜寻的关键词),要求输入keywords.txt中每个关键词在源文件中呈现的行号。


举个例子,如果keywords.txt中有一个关键词是c++,在text4search.txt中第1,7,9,43,543,586,2445行都呈现了c++,那么应该输入c++:{1 7 9 43 543 586 2445 }

先上完整版代码(代码已在vscode c++11规范运行胜利)

#include <algorithm>#include <fstream>#include <iostream>#include <map>#include <sstream>#include <vector>using namespace std;// 报错函数void error(const char *p, const char *p2 = "") {    std::cerr << p << ' ' << p2 << std::endl;    std::exit(1);}map<string, vector<int>> keyCount(ifstream &inputFile, vector<string> &keywords) {    map<string, vector<int>> resultMap;    string line;    int lineNum = 0;    while (getline(inputFile, line)) {        ++lineNum;        // 读入文件的每一行        std::istringstream sin(line);        string word;        //每行单词一一查看是不是在keywords外面呈现,而不是统计keyword在一行中呈现的次数        //这样不必独自写一个函数统计一行中某个关键词呈现次数,也可能呈现一次关键词就压入行号一次        while (sin >> word) {            auto it = find(keywords.begin(), keywords.end(), word);            if (it != keywords.end()) {                resultMap[word].push_back(lineNum);            }        }    }    return resultMap;}int main() {    // 建设三个文件流对象并关联相应文件,留神这里大家要批改为本人的文件门路(或者应用命令行输出)    std::ifstream fin1;    fin1.open("D:/University/Code/cpp_vscode/lab4/keywords.txt"); // 和keywords.txt建设关联    if (!fin1) {        error("cannot open input file", "D:/University/Code/cpp_vscode/lab4/keywords.txt");    }    std::ifstream fin2;    fin2.open("D:/University/Code/cpp_vscode/lab4/text2search.txt"); // 和text2search.txt建设关联    if (!fin2) {        error("cannot open input file", "D:/University/Code/cpp_vscode/lab4/text2search.txt");    }    std::ofstream fout;    fout.open("D:/University/Code/cpp_vscode/lab4/result.txt"); // 和result.txt建设关联    if (!fout) {        error("cannot open output file", "D:/University/Code/cpp_vscode/lab4/result.txt");    }    // 将keywords存入keys这个字符串向量中(用fin1)    vector<string> keys;    string s;    while (fin1 >> s) {        keys.push_back(s);    }    fin1.close();    // 利用fin2和keys产生后果map    map<string, vector<int>> result = keyCount(fin2, keys);    fin2.close();    //输入,因为map会主动依照字典序排序,而行号也是依照升序逐行检测,因而输入不必再排序    map<string, vector<int>>::iterator it1 = result.begin();    while (it1 != result.end()) {        fout << it1->first << " : " << "{";        //it1->second是vector<int>类型,不能间接用fout输入        for (const auto &it2 : it1->second) {            fout << it2 << ",";        }        fout << "}" << endl;        it1++;    }    fout.close();    system("pause");    return 0;}

上面联合代码讲一讲我的思路。
这道题的难点在于如何选取正确、高效的存储办法和搜寻办法:
显然咱们不能简略的挨个读取text中的单词,找到一个关键词就输入一个行号——这样输入的行号是凌乱的。

基本思路

  1. 采纳vector<string>贮存所有keywords;
  2. 采纳map<string,vector<int>>贮存每个关键词到所呈现行号的映射(因为关键词呈现的行号有多个,因而采纳vector<int>贮存行号组成的整数型向量)

具体代码
首先,这里波及到文件的输入输出,必定是要用到<fstream>的,而后自定义文件输入输出流对象并链接到对应的文件:

// 建设三个文件流对象并关联相应文件,留神这里大家要批改为本人的文件门路(或者应用命令行输出)    std::ifstream fin1;    fin1.open("D:/University/Code/cpp_vscode/lab4/keywords.txt"); // 和keywords.txt建设关联    if (!fin1) {        error("cannot open input file", "D:/University/Code/cpp_vscode/lab4/keywords.txt");    }    std::ifstream fin2;    fin2.open("D:/University/Code/cpp_vscode/lab4/text2search.txt"); // 和text2search.txt建设关联    if (!fin2) {        error("cannot open input file", "D:/University/Code/cpp_vscode/lab4/text2search.txt");    }    std::ofstream fout;    fout.open("D:/University/Code/cpp_vscode/lab4/result.txt"); // 和result.txt建设关联    if (!fout) {        error("cannot open output file", "D:/University/Code/cpp_vscode/lab4/result.txt");    }

用文件输出流对象fin每次读入text中的一行,再用字符串输出流对象sin一一读入该行的单词:

int lineNum = 0;    //读入文件的每一行    while (getline(inputFile, line)) {        ++lineNum;        std::istringstream sin(line);//新建sin筹备读入每行中的单词

接下来,咱们的关键点在于,如何用比拟好的办法搜寻并贮存好每个关键词呈现的行号呢?
一般而言,有两种搜寻思路:

  • 每次读取一个keyword,在text全文中搜寻这个keyword在哪些行呈现了,记录行号;
  • 每次读取text中的一行,再一一读取这一行中的每个词,最初搜寻这个词是不是呈现在keywords.txt中,如果呈现,则记录该行的行号;

那么,哪一种搜寻办法更好更高效呢?显然是第二种。第一种办法思路简略,但每次搜寻全文的代价太高。以下是我采纳第二种搜寻的思路的代码:

//sin读入每行中的每个单词while (sin >> word) {            //应用find函数和迭代器搜寻每个sin读入的单词是否呈现在keywords这个vector中            auto it = find(keywords.begin(), keywords.end(), word);            if (it != keywords.end()) {                //如果找到了,那么将这个关键词对应的行号存入map中                resultMap[word].push_back(lineNum);            }

走到这里,曾经根本胜利啦!最初只须要把咱们的答案正确输入,这里我应用迭代器it1输入map中的关键词,因为map中的value值理论是vector类型,不能简略应用it1->second输入,因而咱们再定义一个it2输入vector<int>中的内容

map<string, vector<int>>::iterator it1 = result.begin();    while (it1 != result.end()) {        fout << it1->first << " : " << "{";        //it1->second是vector<int>类型,不能间接用fout输入        for (const auto &it2 : it1->second) {            fout << it2 << ",";        }        fout << "}" << endl;        it1++;    }

功败垂成!