问题描述
|
我有一个模式匹配例程,该例程根据用于请求命令的URL从std :: map查找值。 URL映射表中填充了以下值:
// Assume this->commands_ is defined elsewhere as std::map<std::string,int>
// Providing a number of URL examples to give an idea of the structure of
// the URLs
this->commands_[\"/session\"] = 1;
this->commands_[\"/session/:sessionid/url\"] = 2;
this->commands_[\"/session/:sessionid/back\"] = 3;
this->commands_[\"/session/:sessionid/forward\"] = 4;
this->commands_[\"/session/:sessionid/element\"] = 5;
this->commands_[\"/session/:sessionid/element/:id/text\"] = 6;
this->commands_[\"/session/:sessionid/element/:id/value\"] = 7;
每个网址格式中的标记(由前面的\':\'指定)在查找例程的调用中被替换为实际值(例如actual1ѭ),但它们是我需要保留的命名参数。上面示例中的命名令牌列表并不详尽,并且在上面列出的位置中可能还有其他命名令牌。请注意,令牌值是十六进制编码的数字。
目前,我正在遍历映射的键,用regex值替换替换令牌,并使用std :: tr1 regex类对请求的值执行regex匹配,将匹配的令牌名和值捕获到向量中。该代码在功能上与此等效(为清楚起见,该代码比通常编写的代码更为冗长):
// Assume \"using namespace std;\" has been declared,// and appropriate headers #included.
int Server::LookupCommand(const string& uri,vector<string>* names,vector<string>* values) {
int value = 0;
// Iterate through the keys of the map
map<string,int>::const_iterator it = this->commands_.begin();
for (; it != this->commands_.end(); ++it) {
string url_candidate = it->first;
// Substitute template parameter names with regex match strings
size_t param_start_pos = url_candidate.find_first_of(\":\");
while (param_start_pos != string::npos) {
size_t param_len = string::npos;
size_t param_end_pos = url_candidate.find_first_of(\"/\",param_start_pos);
if (param_end_pos != string::npos) {
param_len = param_end_pos - param_start_pos;
}
// Skip the leading colon
string param_name = url_candidate.substr(param_start_pos + 1,param_len - 1);
names->push_back(param_name);
url_candidate.replace(param_start_pos,param_len,\"([0-9a-fA-F-]+)\");
param_start_pos = url_candidate.find_first_of(\":\");
}
tr1::regex matcher(\"^\" + url_candidate + \"$\");
tr1::match_results<string::const_iterator> matches;
if (tr1::regex_search(uri,matches,matcher)) {
size_t param_count = names->size();
for (unsigned int i = 0; i < param_count; i++) {
// Need i + 1 to get token match; matches[0] is full string.
string param_value = matches[i + 1].str();
values->push_back(param_value);
}
found_value = it->second;
break;
}
}
return value;
}
请注意,我没有使用Boost库,也不允许我在项目中使用它们。
这段代码令我感到效率低下,因为我每次都在遍历地图的各个键,但是我却无法看清树木繁茂的森林,并且遇到了困难与替代品。尽管描述听起来有些荒谬,但我实际上试图构建的是基于键的正则表达式匹配而不是完全匹配的映射查找。如何提高效率?我在设计此功能时忽略了什么模式?
解决方法
以我的方式看,您可以将URL分成其组成部分(可能使用此处的建议之一),然后使用决策树找到正确的模式。
在这棵树中,每个节点将是一个与您的URL的特定组成部分匹配的正则表达式,而叶子将是您当前存储在地图中的值:
session
| \\
| 1
|
([0-9a-fA-F-]+)
/ | \\
/ | \\
url back element
| | | \\
| | | 5
2 3 |
([0-9a-fA-F-]+)
上面是您的示例树的一部分。您必须使用自定义数据结构来实现树,但这很简单。
, 与其使用特定值替换模式中的:session_id和:id令牌,然后进行匹配,不如采用候选者并对其进行正则表达式替换,以占位符(session_id和id)替换特定值?然后,您可以直接在地图中查找通用字符串。