我正在尝试解析基于文本的协议的 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>
但基本建议是相同的:
要管理复杂性,您需要将任务分离为正交子任务。