【问题标题】:Efficient string to key matching in an unordered_map?unordered_map中键匹配的有效字符串?
【发布时间】:2014-05-19 01:14:23
【问题描述】:

将这些字符串映射到函数的最有效方法是哈希表:

std::string a="/foo/", b="/foo/car/", c="/foo/car/can/", d="/foo/car/haz/";

不幸的是,当您想要匹配最简单的模式时,事情变得更加复杂:

/foo/[a-Z|0-9]+>/
/foo/[a-Z|0-9]+>/bar/[a-Z|0-9]+/

有人告诉我,<regex> 库对我的需求来说太过分了;而且它的开销是相当大的。

在这里使用哈希表 (std::unordered_map) 可能是一个有效的选择; [a-Z|0-9]+ 在开关/案例中的单个解析中被检查。参数的数量(在 / 上拆分)并使用 / 的数量,然后使用任意数量的参数来决定采用哪条路径:

"/foo/"                  => {<function>, "/foo/can/", "/foo/[a-Z|0-9]+/bar/"}
"/foo/xflkjkjc34v"       => {<function>, "/foo/can/", "/foo/[a-Z|0-9]+/bar/"}
"/foo/can"               => {<function>, "/foo/can/", "/foo/[a-Z|0-9]+/bar/"}
"/foo/vxcvxc86vzxc/bar/" => {<function>, "/foo/[a-Z|0-9]+/bar/haz"}

有可能实现;但这是最好的方法吗?

【问题讨论】:

  • 你只有两个箱子(有和没有栏)吗?你需要它有多高效?拆分方法有什么问题?
  • 如果它实际上看起来像您的第二个代码块中的示例,您只需计算/ 的数量。如果映射的函数不依赖于映射它的键(意味着只有两个函数,每个函数对应一个案例),那么您甚至不需要哈希表。
  • 否;这些只是例子。在实践中会有更多的组合; RHS 支持数以万计的模式。编辑:我会将示例修改为更具体。
  • 你打算写一个有几万例的开关吗?
  • 我有一个解析实现输出哪些段(在/上分区)在[a-Z]+[0-9]*范围内;并且在[0-9]+ 范围内。

标签: c++ regex c++11 string-matching unordered-map


【解决方案1】:

除了我的评论之外,我认为这是解决您的问题的简单且合理有效的解决方案。这是一个伪代码,因为我不知道您的问题的具体情况(例如您要映射的函数类型等)。

#define MAX_SEGMENTS 255
#define LABEL_LENGTH 10
#define KEY_LENGTH (MAX_SEGMENTS*LABEL_LENGTH)
#define LABEL_FORMAT "%10u"

// ------------------------------------------------------------------------

/**
 * Simple segment defined by position and length in a string.
 */
struct Segment
{
    unsigned pos;
    unsigned len;
};

/**
 * Example of container for regexps. 
 * This could be a tree if you had a nested structure among your regexps.
 * MyRegexp is an object that defines match( const char* segment, unsigned len )
 */
std::vector<MyRegexp> regexps;

/**
 * Mapped functions are in an unordered_map indexed by keys typically built in 
 * parse_segments below.
 */
std::unordered_map<std::string,Function*> mapped_fun;

// ------------------------------------------------------------------------

void split_address( const std::string& address, std::vector<Segment>& segments )
{
    // Split address into segments separated by '/'
}

void parse_segments( const std::string& address, const std::vector<Segment>& segments, char *key )
{
    // key should be of length MAX_SEGMENTS*LABEL_LENGTH.

    // Loop over all regular expressions for each segment.
    // If some regular expressions match a subset of others, then 
    // you have a tree structure among your regexps and you can 
    // exploit this structure to match your segments faster.

    // Here is an example of pseudo-code to create your key, assuming 
    // that you have a vector of regexps.   
    static char buf[ LABEL_LENGTH+1 ];
    for ( unsigned i = 0; i < regexps.size(); ++i )
    if ( regexps[i].match( &address[segments[i].pos], segments[i].len ) )
    {
            sprintf( buf, LABEL_FORMAT, i );
            memcpy( key+LABEL_LENGTH*i, buf, LABEL_LENGTH );
    }
}

Function* map_address( const std::string& address )
{
    // Split address into segments
    std::vector<Segment> segments;
    split_address( address, segments );

    // Match segments to regexps
    static std::string key; key.resize(KEY_LENGTH);
    parse_segments( address, segments, &key[0] );

    // Map address to function
    return mapped_fun.find(key) == mapped_fun.end() ? 
        nullptr : mapped_fun[key];
}

【讨论】:

    【解决方案2】:

    一个理想的数据结构应该是一个 trie,其中每个斜杠分隔的段都与unordered_map 中的最后一个无通配符字符串匹配,甚至是排序的vector(可以在 O(1) 中完成)或 O(logN) ),然后如果没有找到匹配的正则表达式 vector (您可能需要一一尝试 - O(N))。根据您的性能需求,您甚至可以通过将常量字符串视为正则表达式并始终在 trie 中的每个节点上进行 O(N) 搜索来简化事情。

    +----------+     +---------------+                   +-----------+
    | fixed:   |     | fixed:        |                   | fixed:    |
    |    foo  -+---->|    bar       -|---> fn_foo_bar  --|   xxx    -|---> fn_foo_X_xxx
    |    abc  -+-    |               |                /  |           |
    | regexp:  | \   | regexp:       |               /   | regexp:   |
    +----------+  |  |    [A-Z0-9]+ -|---------------    +-----------+
                  |  +---------------+
                  |
                  \->+---------------+
                     | fixed:        |
                      ...
    

    如果您对固定和正则表达式组件的潜在变体数量有更具体的了解,您可能可以进一步优化这一点,但这是具有合理可扩展性的通用解决方案。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-14
    • 1970-01-01
    • 2019-05-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 2017-10-11
    相关资源
    最近更新 更多