0

我正在尝试解析基于文本的协议的 SIP 协议(​​类似于 HTTP)的标头数据包。标题中的字段没有顺序。例如:如果有 3 个字段 f1、f2 和 f3,它们可以按任意顺序出现任意次数,例如 f3、f2、f1、f1。

这增加了我的解析器的复杂性,因为我不知道哪个会先出现。

我应该怎么做才能克服这种复杂性?

4

2 回答 2

1

最终,您只需将处理与收货顺序分离。为此,有一个在遇到字段时重复的循环,并在循环内确定它是哪种字段类型,然后分派给该字段类型的处理。如果您可以立即处理字段,但如果您需要保存为字段类型提供的潜在多个值,您可能 - 例如 - 将它们放入字段名称或 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....
于 2012-06-05T12:27:16.107 回答
0

托尼已经给出了主要想法,我会更具体。

解析的基本思想是一般分为几个阶段。在您的情况下,您需要将词法分析部分(提取标记)与语义部分(作用于它们)分开。

你可以以不同的方式进行,因为我更喜欢结构化的方法,让我们假设我们有一个简单的结构来表示标题:

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");
 }

然后您遍历标题的行,并为每一行分隔名称和值并调用此函数。

明显有改进:

  • 您可以使用仿函数映射而不是 if 链,或者您可以完全标记源并将字段存储在std::map<std::string, std::string>
  • 您可以使用状态机技术立即对其进行操作而无需复制

但基本建议是相同的:

要管理复杂性,您需要将任务分离为正交子任务。

于 2012-06-05T12:40:08.070 回答