我正在尝试解析SIP协议的标头包(类似于HTTP),这是一种基于文本的协议。 标题中的字段没有订单。 例如:如果有3个字段,f1,f2和f3,它们可以任意顺序出现f3,f2,f1,f1。
这增加了我的解析器的复杂性,因为我不知道哪个会先出现。
我应该怎样做才能克服这种复杂性?
答案 0 :(得分:1)
最终,您只需将处理与收据顺序分离即可。要做到这一点,请在遇到字段时重复循环,并在循环内确定它是哪种字段类型,然后调度到该字段类型的处理。如果您可以立即处理这些字段,但是如果您需要保存为字段类型提供的潜在多个值,您可以(例如)将它们放入vector
或甚至共享的multimap
键入字段名称或ID。
的伪代码:
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....
答案 1 :(得分:0)
Tony已经提出了主要想法,我会更具体。
解析的基本思想是它通常分为几个阶段。在你的情况下,你需要将lexing部分(提取标记)与语义部分(作用于它们)分开。
你可以用不同的方式进行,因为我更喜欢结构化方法,让我们假设我们有一个简单的结构表示头部:
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>
但基本建议是一样的:
要管理复杂性,您需要在正交子任务中分离任务。