符号表
符号表就是set和map,在c++中有基于红黑树实现的map,set,以及基于hash函数实现的unordered_map,unordered_set。
本文代码用到的符号表是我自己的实现,根据《算法4》的实现方法。
黑名单,白名单
黑名单就是名单上有的就过滤掉,输出名单上没有的。
白名单就是输出名单上有的,名单上没有的过滤掉。
CSV文件构造符号表
csv文件是一种以“,”为分隔符的格式化的储存文件,我们可以通过指定csv文件中的两个列来构建一个对应符号表。
查询索引符号表
符号表不一定是一对一,也可能是一对多,比如水果可以对应:苹果,香蕉,梨,菠萝,荔枝,西瓜等。根据这样规则的符号表就是查询索引表。
文件查询索引符号表
就是将文件内容当作key,将文件名当作val,类似文件搜索时,用文件的内容查看含有此内容的文件。
实现难点
这里最easy的难点是CSV文件的分隔符“,”,因为C++的流默认是用空格或换行符截断,同时C++没有
string 的 splite() 方法,好在有 getline() 函数,可以指定分隔符。
我遇到的比较困难的问题是做索引符号表,其结构是hashmap<string, vector<string>>,在我的hashmap实现中无法返回vector的引用,因为C++没有null类型,在hashmap不含有某个key的时候,get()函数返回为{ false, val() },这种 val() 右值无法转换为引用。
没有引用就无法插入元素。所以,只能引入令人头痛的指针。变成hashmap<string, vector<string>*>,的结构,解决了问题。只不过函数最后要delete掉所有new出来的内存。
性能问题
测试遇到一个含有3M大小的txt文件,构建查询索引符号表的时候,需要3,4秒才能完成,慢的令我怀疑我是否是用的C++,如果只有几万,几十万个数据,就慢成这样,我为啥用C++?
经过单步调试,发现问题出现在hashmap的增长方法,它需要不停的增长,导致不停的拷贝重建。如同vector 的增长问题,如果能事先预测hashmap到底有多少元素,就能解决。lookUpIndex() 函数中hashmap构建的注释部分有所体现。
以下是参考代码
//输出流去重复
void DeDup()
{
ST::hashSet<std::string> set;
std::string keys;
while (std::cin >> keys)
{
if (!set.contains(keys))
{
set.add(keys);
std::cout << keys << " ";
}
}
}
// 白名单
void whiteFilter(const std::string &file)
{
ST::hashSet<std::string> set;
std::string keys;
std::ifstream fileword(file);
while (fileword >> keys)
{
set.add(keys);
}
fileword.close();
while (std::cin >> keys)
{
if (set.contains(keys))
{
std::cout << keys << " ";
}
}
}
// 黑名单
void blackFilter(const std::string &file)
{
ST::hashSet<std::string> set;
std::string keys;
std::ifstream fileword(file);
while (fileword >> keys)
{
set.add(keys);
}
fileword.close();
while (std::cin >> keys)
{
if (!set.contains(keys))
{
std::cout << keys << " ";
}
}
}
// CSV文件构造符号表,参数:CSV文件名,key的下标,value的下标
void lookUpCsv(const std::string &file, const std::string &kField, const std::string &vField)
{
int keyField = std::stoi(kField);
int valField = std::stoi(vField);
std::ifstream files(file);
std::stringstream words;
ST::LPHashST<std::string, std::string> st;
std::vector<std::string> tokens;
tokens.reserve(8);
std::string line;
std::string word;
while (std::getline(files, line))
{
// std::stringstream words(line);
words.str(line);
while (std::getline(words, word, ','))
{
tokens.push_back(word);
}
st.put(tokens[keyField], tokens[valField]);
tokens.clear();
words.clear();
}
files.close();
while (std::cin >> word)
{
std::pair<bool, std::string> temp = st.get(word);
if (temp.first)
{
std::cout << temp.second << "\n";
}
}
}
//构建查询索引符号表,参数为数据文件名,分隔符
void lookUpIndex(const std::string &files, const std::string &split)
{
std::ifstream file(files);
std::stringstream line;
std::vector<std::string> keyVec;
keyVec.reserve(32);
char sp = split.at(0);
ST::LPHashST<std::string, std::vector<std::string> *> st; //(12289);
ST::LPHashST<std::string, std::vector<std::string> *> ts; //(393241);
std::string words;
std::string word;
std::string val;
std::string key;
while (std::getline(file, words))
{
line.str(words);
while (std::getline(line, word, sp))
{
keyVec.push_back(word);
}
key = keyVec[0];
for (int i = 1; i != keyVec.size(); ++i)
{
val = keyVec[i];
if (!st.contains(key))
{
st.put(key, new std::vector<std::string>());
st.get(key).second->reserve(16);
}
if (!ts.contains(val))
{
ts.put(val, new std::vector<std::string>());
ts.get(val).second->reserve(16);
}
st.get(key).second->push_back(val);
ts.get(val).second->push_back(key);
}
keyVec.clear();
line.clear();
}
file.close();
std::string query;
while (std::getline(std::cin, query))
{
if (st.contains(query))
{
for (auto &&i : *st.get(query).second)
{
std::cout << " " << i << "\n";
}
}
if (ts.contains(query))
{
for (auto &&i : *ts.get(query).second)
{
std::cout << " " << i << "\n";
}
}
}
auto delstVec = st.keyVec();
auto deltsVec = ts.keyVec();
for (auto &&i : delstVec)
{
delete st.get(i).second;
}
for (auto &&i : deltsVec)
{
delete ts.get(i).second;
}
}
//构建查询索引符号表,参数为数据文件名的数量-1,数据文件名字符组。
void fileIndex(int argc, char *argv[])
{
ST::LPHashST<std::string, ST::hashSet<std::string> *> st;
std::string filename;
std::string word;
std::fstream file;
for (int i = 1; i != argc; ++i)
{
file.open(argv[i]);
while (file >> word)
{
if (!st.contains(word))
{
st.put(word, new ST::hashSet<std::string>());
}
st.get(word).second->add(argv[i]);
}
file.close();
}
std::string query;
while (std::cin >> query)
{
if (st.contains(query))
{
auto prtvec = st.get(query).second->keyVec();
for (auto &&i : prtvec)
{
std::cout << " " << i << "\n";
}
}
}
auto delvec = st.keyVec();
for (auto &&i : delvec)
{
delete st.get(i).second;
}
}