2011-06-25 12 views
4

私はコマンドを要求するために使用されたURLに基​​づいてstd :: mapから値を検索するパターンマッチングルーチンを持っています。 URLマッピングテーブルは同じ値で満たされている各URLパターンにC++で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; 

トークン(で指定された先行する「:」)ルックアップルーチンの呼び出しで実際の値に置き換えられている(例えば、"/session/1234-8a0f/element/5bed-6789/text")私が保持する必要のある名前の付いたパラメータです。上の例の名前付きトークンのリストは網羅的ではなく、上記の位置に他の名前付きトークンがあるかもしれません。トークン値は16進数の数字であることに注意してください。

現在は、置換トークンを正規表現値で置き換え、std :: tr1 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ライブラリを使用していないことに注意してください、また私は、このプロジェクトのためにそれらを使用することを許可しています。

このコードは毎回地図のキーを繰り返しているので私には非常に効率的ではないと感じていますが、私は木々の森を見ることができず、苦労しています代替案が出てくる。説明は無意味に聞こえるが、私が本質的に構築しようとしているのは、完全一致ではなく、キーの正規表現マッチングに基づくマップ参照である。これをより効率的にするにはどうすればいいですか?この機能のデザインで私はどのパターンを見落としましたか?

+1

興味深い質問です。しかし、最初から、 'std :: map'はここでは正しいデータ構造ではないことに注意してください。それは正確なキーからキーの相対的な順序関係に基づく値へのマッピングを提供する。まったく異なるもの、つまりパターンマッチングの関係に基づいた値へのキーのマッピングが必要です。 –

+2

地図が正しいデータ構造ではないとします。私はベクトルを使ってiteratingするベクトルの機能的な同等物としてここで使用しています。この関数が有効になったとき、マップは正しい構造でしたが、要件が変更された後、別のデータ構造を使用するようにコードを修正していませんでした。最終的にはそれが問題です。使用する正しいデータ構造は何ですか? – JimEvans

答えて

6

私はそれを見て、(おそらくhereの提案の1つを使用して)そのコンポーネントにURLを分割してから、decision treeを使用して正しいパターンを見つけることができます。

このツリーでは、すべてのノードがあなたのURLの特定のコンポーネントにマッチする正規表現となり、葉はあなたが現在あなたのマップに格納値は次のようになります。

        session 
            | \ 
            | 1 
            | 
           ([0-9a-fA-F-]+) 
          / |  \ 
          / |  \ 
          url  back element 
          |  |  |  \ 
          |  |  |  5 
          2  3  | 
             ([0-9a-fA-F-]+) 

上記は一部であり、あなたの例のための木の。ツリーを実装するにはカスタムデータ構造を使用する必要がありますが、それはむしろ単純です。

+0

私はこれと他の答えとのハイブリッドアプローチを取ったが、これは私が逃した洞察である。 – JimEvans

1

パターン内の:session_idと:idトークンを特定の値に置き換えて、マッチングを実行する代わりに、候補を取ってregexを使って特定の値をプレースホルダ(session_idとid)に置き換えてください。次に、マップ内のジェネリック化された文字列を直接探すことができます。

+0

このアプローチには2つの課題があります。まず、 "sessionid"と "id"はURL内のそれらの位置に現れる唯一のトークン名ではありません。 URLの例ではそうしたように見えます。第2に、トークン値は16進数でエンコードされているため、アルファベットも使用できます。私は質問とサンプルコードを編集して、それをより明確にしました。 – JimEvans

関連する問題