テキストベースのプロトコルである SIP プロトコル (HTTP に類似) のヘッダー パケットを解析しようとしています。ヘッダーのフィールドには順序がありません。例: 3 つのフィールド、f1、f2、および f3 がある場合、それらは f3、f2、f1、f1 のように、任意の順序で何回でも来ることができます。
どちらが最初に来るかわからないので、これは私のパーサーの複雑さを増しています。
この複雑さを克服するにはどうすればよいですか?
最終的には、処理を受信の順序から切り離すだけで済みます。これを行うには、フィールドが検出されている間繰り返すループを作成し、ループ内でそれがどのフィールド タイプであるかを判別してから、そのフィールド タイプの処理にディスパッチします。フィールドをすぐに処理できる場合は素晴らしいですが、フィールドタイプに指定された潜在的に複数の値を保存する必要がある場合は、たとえば、それらをフィールド名または ID の共有キーvector
または共有キーに入れることができます。multimap
擬似コード:
Field x;
while (x = get_next_field(input))
{
switch (x.type())
{
case Type1: field1_values.push_back(x.value()); break;
case Type2: field2 = x.value(); break; // just keep the last value seen...
default: throw std::runtime_error("unsupported field type");
}
}
// use the field1_values / field2 etc. variables....
トニーはすでに主なアイデアを示しています。より具体的に説明します。
構文解析の基本的な考え方は、一般にいくつかのフェーズに分かれているということです。あなたの場合、字句部分(トークンの抽出)をセマンティック部分(それらに作用する)から分離する必要があります。
私は構造化されたアプローチを好むので、さまざまな方法で進めることができます。ヘッダーを表す単純な構造体があると仮定しましょう。
struct SipHeader {
int field1;
std::string field2;
std::vector<int> field3;
};
ここで、フィールド名とその値を受け取り、SipHeader
構造体の対応するフィールドを適切に埋める関数を作成します。
void parseField(std::string const& name, std::string const& value, SipHeader& sh) {
if (name == "Field1") {
sh.field1 = std::stoi(value);
return;
}
if (name == "Field2") {
sh.field2 = value;
return;
}
if (name == "Field3") {
// ...
return;
}
throw std::runtime_error("Unknown field");
}
次に、ヘッダーの行を反復処理し、各行で名前と値を分離して、この関数を呼び出します。
明らかに改良点があります。
std::map<std::string, std::string>
しかし、本質的なアドバイスは同じです:
複雑さを管理するには、タスクを直交するサブタスクに分ける必要があります。