我有一个模式匹配例程,它根据用于请求命令的 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;
每个 URL 模式中的标记(由前面的“:”指定)在查找例程的调用中被替换为实际值(例如,"/session/1234-8a0f/element/5bed-6789/text"
),但它们是我需要保留的命名参数。上述示例中的命名 token 列表并不详尽,上面列出的位置可能还有其他命名 token 。请注意, token 值是十六进制编码的数字。
目前,我正在遍历映射的键,用正则表达式值替换替换标记,并使用 std::tr1 正则表达式类对请求的值执行正则表达式匹配,将匹配的标记名称和值捕获到载体。该代码在功能上等同于此(为清楚起见,代码比通常编写的代码更冗长):
// 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 库,也不允许我在这个项目中使用它们。
这段代码对我来说效率极低,因为我每次都在遍历 map 的键,但我正遭受着只见树木不见森林的痛苦,而且我遇到了困难提出替代方案。虽然描述听起来很荒谬,但我实际上试图构建的是基于键的正则表达式匹配而不是精确匹配的 map 查找。我怎样才能使它更有效率?我在设计此功能时忽略了哪些模式?
最佳答案
按照我的看法,您可以将 URL 拆分成它的组件(也许使用 here 中的建议之一),然后使用 decision tree找到正确的模式。
在这棵树中,每个节点都是一个正则表达式,与您的 URL 的特定部分相匹配,叶子是您当前存储在 map 中的值:
session
| \
| 1
|
([0-9a-fA-F-]+)
/ | \
/ | \
url back element
| | | \
| | | 5
2 3 |
([0-9a-fA-F-]+)
以上是您示例的树的一部分。您必须使用自定义数据结构来实现树,但这相当简单。
关于c++ - 有没有比迭代更好的方法在 C++ 中执行 URL 模式匹配?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6479136/