共计 3871 个字符,预计需要花费 10 分钟才能阅读完成。
博主刚开始学习 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 中的单词,找到一个关键词就输入一个行号——这样输入的行号是凌乱的。
基本思路
- 采纳
vector<string>
贮存所有 keywords; - 采纳
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++; | |
} |
功败垂成!