我今天早些时候遇到了this question:
示例输入: 我遇到了乔和吉尔然后我们去购物了 示例输出: [TOP [S [S [NP [PRP I]] [VP [VBD运行] [PP [IN进入] [NP [NNP Joe] [CC和[NNP Jill]]]]] [CC和] [S [ADVP [RB 然后]] [NP [PRP我们]] [VP [VBD去] [NP [NN购物]]]]]]
我打算建议简单地将预期输出(因为它看起来像一个s表达式)解析为一个对象(在我们的例子中是一个树),然后使用简单的LINQ方法来处理它。但令我惊讶的是,我无法找到C#s-expression解析器。
我唯一能想到的是使用Clojure解析它,因为它编译成clr,我不确定它是一个很好的解决方案。
顺便说一句,我不介意输出dynamic
类型的答案。我在这里找到的答案只是用于反序列化为特定模式。
总结我的问题: 我需要在C#中反序列化s表达式(序列化对于这个问题的未来读者来说会很好)
答案 0 :(得分:7)
看起来您需要以下形式的数据结构:
public class SNode
{
public String Name { get; set; }
private readonly List<SNode> _Nodes = new List<SNode>();
public ICollection<SNode> Nodes { get { return _Nodes; } }
}
表单
的序列化程序public String Serialize(SNode root)
{
var sb = new StringBuilder();
Serialize(root, sb);
return sb.ToString();
}
private void Serialize(SNode node, StringBuilder sb)
{
sb.Append('(');
sb.Append(node.Name);
foreach (var item in node.Nodes)
Serialize(item, sb);
sb.Append(" )");
}
以下形式的反序列化:
public SNode Deserialize(String st)
{
if (String.IsNullOrWhiteSpace(st))
return null;
var node = new SNode();
var nodesPos = String.IndexOf('(');
var endPos = String.LastIndexOf(')');
var childrenString = st.SubString(nodesPos, endPos - nodesPos);
node.Name = st.SubString(1, (nodesPos >= 0 ? nodePos : endPos)).TrimEnd();
var childStrings = new List<string>();
int brackets = 0;
int startPos = nodesPos;
for (int pos = nodesPos; pos++; pos < endPos)
{
if (st[pos] == '(')
brackets++;
else if (st[pos] == ')')
{
brackets--;
if (brackets == 0)
{
childStrings.Add(st.SubString(startPos, pos - startPos + 1));
startPos = pos + 1;
}
}
}
foreach (var child in childStrings)
{
var childNode = Deserialize(this, child);
if (childNode != null)
node.Nodes.Add(childNode);
}
return node;
}
但是,如果还没有测试甚至编译过这段代码,那么它或多或少都会起作用。
答案 1 :(得分:2)
我编写了一个开源的S-Expression解析器,可用S-Expression.NET。由于它使用OMeta#生成解析器,因此您可以快速使用它来添加新功能。