智能信息检索这门课的第一个上机实验:
问题表述如下:
1.对硬盘目录中的10个文本文件(doc01.txt~doc10.txt),在内存中建立倒排索引
2.构建索引系统,输入查询词,输出包含查询词的文档ID
思路
要构建倒排索引,首先我们对每个文档编号(设置ID),这里我们可以设置一个名为Doc的结构体,用于绑定文档和他的ID,然后将每个文档放入数组中,生成我们所需要构建倒排索引的文档集。(这里我们使用STL中的vector),之后进行最重要的构建倒排索引的操作(详细步骤见该模块说明),最后我们简单的写一个查询函数就完成我们的任务啦。
一、构建文档
1.给出我们要构建倒排的文档名
static vector<string> files={
"doc1.txt","doc2.txt", "doc3.txt",
"doc4.txt","doc5.txt", "doc6.txt",
"doc7.txt","doc8.txt","doc9.txt","doc10.txt" };
2.建立文档结构体,保存其文档名和文档ID
struct Doc {
string docName;//文档名
int docID;//文档ID
Doc() { docName = "", docID = -1; }
};
3.构建文档集
vector<Doc> Docs;//文档集
void makeDocs(vector<Doc> &Docs){//生成文档集
Doc *doc = new Doc;
//将文档依次编号,生成文档集
for (int i = 0; i < files.size(); ++i) {
doc->docName = files[i];
doc->docID = 1+i;
Docs.push_back(*doc);
}
delete doc;
}
3.显示我们构建的文档集(这是一个小小的附加功能)
void showDocs() {//显示文档集
for (int i = 0; i <Docs.size(); ++i) {
cout << "DocName: "<<Docs[i].docName << "\tdocID: " << Docs[i].docID<<endl;
}
}
二、构建倒排索引
详细步骤见注释啊,笔者懒了:
map<string,set<int>> indexList;//倒排记录表
void index(vector<Doc> &Docs) {
for (int i = 0; i < Docs.size(); ++i) {
ifstream in(Docs[i].docName);//依次打开文档
int ch;//用于扫描的字母
string s;//获取到的单词
map<string, set<int>>::iterator it;//用于判定词项是否在倒排记录表中
if (in.is_open()) {
while (!in.eof()) {//一次循环获取一个单词
//找到第一个字母
do {
ch = in.get();//获取一个字符
if (in.eof())break;//遇到文件尾则结束
} while (!isalpha(ch));
if (in.eof())break;//防止后面的空行而不能结束
while (isalpha(ch)) {
ch=tolower(char(ch));//将获取到的字母变为小写
s += ch;//合成单词
ch = in.get();
}
it = indexList.find(s);//判断s是否已经在倒排记录表中
if (it != indexList.end()) {//如果s已经在全体倒排记录表中
it->second.insert(i+1);//只将词项对应的文档编号加入倒排记录表
}
else {//s不在全体倒排记录表中
set<int> pset{i+1};//s词项对应的文档编号放入set
pair<string, set<int>> newTrem(s, pset);//新词项及其倒排记录表
indexList.insert(newTrem);//将新词项及其倒排记录表加入全体倒排记录表
}
s.clear();//清空s,进行下一个单词的操作
}
}
in.close();//关闭文件,以便进行下一个文档的到操作
}
cout << "倒排索引构建成功" << endl;
}
一个附加小功能(显示倒排记录表)
void showIndexList(const map<string, set<int>> indexList){
for (auto c : indexList) {//显示倒排记录表
cout << c.first<<"\t" ;
for (auto c1 : c.second) {
cout << c1 << " ";
}
cout << endl;
}
}
三.查询
void query(string s) {//查询
map<string, set<int>>::iterator it=indexList.find(s);//判断s是否在全体倒排记录表
if (it!= indexList.end()) {//Yes
cout << s << "\t"<<"出现的文档:";
int length = it->second.size();
for (auto c : it->second) {//输出该词项的倒排记录表
cout << c << " ";
}
cout << endl;
}
else {//N0
cout << s << "没有在文档中出现" << endl;
}
}
main函数
int main() {
makeDocs(Docs);
showDocs();
index(Docs);
showIndexList(indexList);
while (true) {
cout << "请输入查询词:";
string s;
cin >> s;
query(s);
cout << endl;
}
}
总结
笔者写该程序用到了,vector,set,map,pair等等STL的功能,set和map的底层都是红黑树,所以每次进行的查询,插入等操作时间复杂度都是灰常的低的。把代码从上到下copy一下就是源代码了,笔者很菜也,肝了很久,给个赞呗。