假设我正在从文件中读取一行:
{Parent{{ChildA}{ChildB}}}
更复杂的例子:
{Parent{{ChildA{ChildC}{ChildD}}{ChildB{ChildE}{ChildF}}}}
用于构造树的语法是什么。
{}
括号内的任何名称都是一个节点,如果在该括号内还有其他节点(括号),那么这些节点就是子节点。
我能够使用计数器解析第一个特定示例,但只能找到节点的文本名称。我该如何解析这个,以便我可以确定哪些节点是彼此的子节点?我似乎无法围绕我将使用的代码。我有一种感觉,我会使用递归。
任何帮助或建议都将不胜感激。
首选C ++。
非常感谢。
答案 0 :(得分:5)
您必须跟踪当前的嵌套。为此,您可以使用堆栈。
每次遇到{
(后跟节点名称),您就知道这是新节点的开始。此新节点是当前节点的子节点。
每次遇到}
时,您都知道当前节点已完成,这意味着您必须告诉程序当前节点现在已更改为当前节点的父节点。
示例:
{A{B{C}{D}{E}}{F{G}{H}}} Stack:
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A // A is root
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, B // B is child of A
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, B, C // C is child of B
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, B, // C has no child, C done
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, B, D // D is child of B
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, B,
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, B, E // E child of B
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, B,
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, // B has no more children, B done
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, F // F child of A
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, F, G // G child of F
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, F,
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, F, H
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A, F,
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack: A,
^
{A{B{C}{D}{E}}{F{G}{H}}} Stack:
^
DONE.
答案 1 :(得分:3)
如果是家庭作业,那么无论如何都不能使用答案来破坏乐趣:
使用Boost Spirit Qi的最小实现:
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
typedef boost::make_recursive_variant<
std::vector<boost::recursive_variant_>,
std::string>::type ast_t;
void dump(const ast_t&);
// adhoc parser rule:
static const qi::rule<std::string::iterator, ast_t()> node =
'{' >> *node >> '}' | +~qi::char_("{}");
int main()
{
std::string input = "{Parent{{ChildA{ChildC}{ChildD}}{ChildB{ChildE}{ChildF}}}}";
std::string::iterator f(input.begin()), l(input.end());
ast_t tree;
if (qi::parse(f, l, node, tree))
dump(tree);
else
std::cerr << "Unparsed: " << std::string(f, l) << std::endl;
}
令人遗憾的是dump
的实施几乎等同于代码量:)
它将打印:
{
Parent
{
{
ChildA
{
ChildC
}
{
ChildD
}
}
{
ChildB
{
ChildE
}
{
ChildF
}
}
}
}
<子>
以下是dump(const ast_t&)
:
struct dump_visitor : boost::static_visitor<>
{
dump_visitor(int indent=0) : _indent(indent) {}
void operator()(const std::string& s) const { print(s); }
template <class V>
void operator()(const V& vec) const
{
print("{");
for(typename V::const_iterator it=vec.begin(); it!=vec.end(); it++)
boost::apply_visitor(dump_visitor(_indent+1), *it);
print("}");
}
private:
template <typename T> void print(const T& v) const
{ std::cout << std::string(_indent*4, ' ') << v << std::endl; }
int _indent;
};
void dump(const ast_t& tree)
{
boost::apply_visitor(dump_visitor(), tree);
}
答案 2 :(得分:2)
由于它是家庭作业,我认为你必须手动实现解决方案,所以你可能想要在解析输入时使用Stack来保存数据。
每次看到{
时,都会创建一个包含数据的新节点,然后将其推送到堆栈中。
每次看到}
时,都会从堆栈中弹出最后一个节点,并将其添加到树形状中。
这种方法需要的另一件事是Node指针,我们称之为currentNode
,这样我们就可以实现实际的层次结构。首先,currentNode
将为空;第一次从堆栈弹出一个节点时,将该节点放入currentNode
。否则,当您弹出一个值时,我们知道堆栈中的下一个节点都有子节点。
我会让你从那里开始使用它,但如果你需要更多,我会站在那里。
答案 3 :(得分:1)
你的语法相对简单。
根据您的示例,节点可以用两种不同的方式声明:
{nodename}
这是一个简单的
{nodename{childnodes}}
这是复杂的
现在把它变成一个更正式的语法我们首先写下组成部分:
"{" nodename "}"
"{" nodename "{" childnodes "}" "}"
然后我们可以定义语法(非终端大写)
Node :: =“{”Nodename“}”| “{”Nodename“{”Childnodes“}” Nodename :: =至少一个字母 Childnodes :: =一个或多个Node
转换成解析器的标准方法(假设您必须手动编写,因为它太小而你会正确地编写它)是编写一个可以解析每个非终端的方法(你看到的是什么) :== sign)的左边。
唯一棘手的问题是你必须编写Nodename函数来检查是否有“{”(在这种情况下节点有一个孩子)或“}”(在这种情况下它没有孩子) )节点名称结束后。
此外,我冒昧地不写下所有可能的ascii字符串作为Nodename。
答案 4 :(得分:0)
想象一下这就是这样的东西(尽管它与您正在阅读的文件是线性的,但只需尝试以这种方式进行可视化)
{
Parent
{
{
ChildA
{
ChildC
}
{
ChildD
}
}
{
ChildB
{
ChildE
}
{
ChildF
}
}
}
}
所以现在更明显的是,每当你得到'{'你有一个孩子。所以,当你得到一个'{'产生一个孩子时,你会失明(递归但如果你读的线太长,那么我建议你去迭代),每当你遇到一个'}'移动一级时(到父母)。
我想你可能会有一个函数,用于向树中添加节点并将树向上移动一级。如果这就是你所拥有的,那么你需要做的就是将各个部分放在一起。
我希望这是有道理的。
答案 5 :(得分:0)
每次找到“{”然后将子项添加到父项时,每次找到“}”时,将当前树设置为父树。
public class Tree
{
public Tree Parent { get; set; }
public string Value { get; set; }
public List<Tree> Children { get; set; }
}
public Tree Parsing()
{
string rawString = this._rawData;
Tree parent = new Tree { Parent = null,Value = "",Children = new List<Tree>()};
Tree child = parent;
foreach (char c in rawString)
{
if (c == '{')
{
child = new Tree { Parent = child, Value = string.Empty, Children = new List<Tree>() };
child.Parent.Children.Add(child);
}
else if (c == '}')
{
child = new Tree { Parent = child.Parent.Parent, Value = string.Empty, Children = new List<Tree>() };
if (child.Parent != null)
{
child.Parent.Children.Add(child);
}
}
else
{
child.Value += c;
}
}
return parent;
}
public void RenderTree(Tree tree, string level)
{
if (tree.Value.Length > 0)
Console.WriteLine(level + tree.Value);
foreach (Tree t in tree.Children)
{
RenderTree(t, level + " ");
}
}