C++ 解析文本以生成树数据结构
假设我正在从文件中读取一行:C++ 解析文本以生成树数据结构,c++,parsing,data-structures,tree,C++,Parsing,Data Structures,Tree,假设我正在从文件中读取一行: {Parent{{ChildA}{ChildB}}} 更复杂的例子: {Parent{{ChildA{ChildC}{ChildD}}{ChildB{ChildE}{ChildF}}}} 这是用来构造树的语法 {}括号内的任何名称都是节点,如果在该括号内有其他节点(括号),则这些节点是子节点 我能够使用计数器解析第一个特定示例,但只能找到节点的文本名称。我如何解析它,以便确定哪些节点是彼此的子节点?我似乎无法将我的思想集中在我将要使用的代码上。我觉得我会使用递归
{Parent{{ChildA}{ChildB}}}
更复杂的例子:
{Parent{{ChildA{ChildC}{ChildD}}{ChildB{ChildE}{ChildF}}}}
这是用来构造树的语法
{}
括号内的任何名称都是节点,如果在该括号内有其他节点(括号),则这些节点是子节点
我能够使用计数器解析第一个特定示例,但只能找到节点的文本名称。我如何解析它,以便确定哪些节点是彼此的子节点?我似乎无法将我的思想集中在我将要使用的代码上。我觉得我会使用递归
任何帮助或建议都将不胜感激
C++优先
非常感谢。因为这是家庭作业,我假设您必须手动实现解决方案,因此您可能希望在解析输入时使用堆栈来保存数据 每次看到
{
时,都会创建一个新节点,其后面有数据,并将其推送到堆栈上
每次看到}
时,都会从堆栈中弹出最后一个节点,并将其添加到树形状中
这种方法还需要一个节点指针,我们称之为currentNode
,这样我们就可以实现实际的层次结构。首先,currentNode
将为空;第一次从堆栈中弹出节点时,将该节点放入currentNode
。否则,当您弹出一个值时,我们知道堆栈上下一个节点的两个子节点都存在
我会让你从那里带着它跑,但如果你需要更多,我会随时待命。你必须跟踪当前的筑巢情况。为此,可以使用堆栈 每次遇到
{
(后跟节点名),您都知道这是新节点的开始。此新节点是当前节点的子节点
每次遇到}
时,您都知道当前节点现在已完成,这意味着您必须告诉您的程序当前节点现在已更改为当前节点的父节点
例如:
{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.
想象一下这是这样的(尽管它是从您正在读取的文件中线性的,但是尝试以这种方式可视化它) 所以现在更明显的是,每当你得到你的{时,你就有了一个孩子。所以,无论何时你得到一个{时,你都会盲目地产生一个孩子(递归地,但如果你正在读取的行太长,那么我建议你通过迭代),无论何时你遇到一个}时,向上移动一级(到父级) 我想你应该有一个功能,可以向树中添加一个节点,并将树向上移动一个级别。如果这就是你所拥有的,那么你所需要做的就是把各个部分放在一起
我希望这有点道理。你的语法比较简单 根据您的示例,可以使用以下两种不同方式之一声明节点:
{nodename}
哪一个是简单的
{nodename{childnodes}}
哪个是复杂的
现在,为了将其转化为一种更正式的语法,我们首先编写以下组成部分:
"{" nodename "}"
"{" nodename "{" childnodes "}" "}"
然后我们可以定义语法(非终结符大写)
节点::=“{”节点名“}”|
{“节点名”{“子节点”}
Nodename::=至少一个字母
子节点::=一个或多个节点
将than转换为解析器的标准方法(假设您必须手工编写,因为它太小了,所以您应该手工编写)是编写一个可以解析每个非终端的方法(您在:==符号左侧看到的内容)
唯一棘手的问题是,您必须编写Nodename函数来检查节点名称后面是否有“{”(在这种情况下,节点有子节点)或“}”(在这种情况下,节点没有子节点)
我还冒昧地没有把所有可能的ascii字符串都写下来作为节点名。如果答案是家庭作业,你无论如何也不能使用,这会破坏乐趣: 使用提升精神气的最低限度实施:
#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;
}
以下是转储(const ast\u t&)的定义:
struct dump\u访问者:boost::static\u访问者
{
dump_visitor(int indent=0):_indent(indent){
void操作符()(const std::string&s)const{print(s);}
模板
void运算符()(常量V&vec)常量
{
印刷品(“{”);
对于(typename V::const_迭代器it=vec.begin();it!=vec.end();it++)
boost::apply_visitor(转储_visitor(_indent+1),*it);
打印(“}”);
}
私人:
模板无效打印(常量T&v)常量
{std::cout每次找到“{”时,将子级添加到父级,然后每次找到“}”时,将当前树设置为父级树
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 + " ");
}
}
公共类树
{
公共树父级{get;set;}
公共字符串值{get;set;}
公共列表子项{get;set;}
}
公共树解析()
{
string rawString=这个;
树父级=新树{parent=null,Value=”“,Children=new List()};
树子=父;
foreach(原始字符串中的字符c)
{
如果(c=='{')
{
child=新树{Parent=child,Value=string.Empty,Children=newlist()};
child.Parent.Children.Add(child);
}
else如果(c=='}')
{
child=新树{Parent=child.Parent.Parent,Value=string.Empty,Children=new List()};
if(child.Parent!=null)
{
child.Parent.Children.Add(child);
}
}
其他的
{
子值+=c;
}
}
返回父母;
}
公共void RenderTree(树、字符串级别)
{
如果(tree.Value.Length>0)
Console.WriteLine(level+tree.Value);
foreach(树中的树t.Children)
{
RenderTree(t,level+);
}
}
这看起来像一个简单的上下文无关文法
{
Parent
{
{
ChildA
{
ChildC
}
{
ChildD
}
}
{
ChildB
{
ChildE
}
{
ChildF
}
}
}
}
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);
}
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 + " ");
}
}