如何从C#中的字符串解析嵌套结构?
我有一个这样结构的字符串如何从C#中的字符串解析嵌套结构?,c#,.net,string,parsing,tree,C#,.net,String,Parsing,Tree,我有一个这样结构的字符串 id i { any data; any [con=tent] id j { any inner data } id k { bla id m { any } thing } } 其中,id NAME{CONTENT}是可嵌套对象的模式。所需的目标如下所示: public class Node { public L
id i {
any data; any [con=tent]
id j {
any inner data
}
id k {
bla
id m {
any
}
thing }
}
其中,id NAME{CONTENT}
是可嵌套对象的模式。所需的目标如下所示:
public class Node {
public List<Node> InnerNodes;
public string Contents;
public string Name;
}
公共类节点{
公共列表节点;
公共字符串内容;
公共字符串名称;
}
如何使用开源软件包使用C#.Net解析此类对象树?您有几个选项:
- 尺寸/速度要求李>
- 文档结构的更精确定义李>
- 格式验证,更具体地说是返回友好 发送给用户的消息
- 优化代码的性能、分配、并发性
public class Node
{
public Node(string name)
{
Name = name;
InnerNodes = new List<Node>();
}
public string Name { get; }
public string Contents { get; set; }
public List<Node> InnerNodes { get; }
}
和一个助手类,用于将输入字符串转换为一系列标记
internal static class Tokenizer
{
public static IEnumerable<Token> Scan (string expression)
{
var words = new Queue<string>
(expression.Split(new[] { ' ', '\n', '\r', '\t' }, StringSplitOptions.RemoveEmptyEntries));
while (words.Any())
{
string word = words.Dequeue();
switch (word)
{
case "id":
yield return new OpenNodeToken(words.Dequeue());
words.Dequeue();
break;
case "}":
yield return new CloseNodeToken();
break;
default:
yield return new ContentToken(word);
break;
}
}
}
}
在这里,我们使用堆栈
在解析子节点之前将父节点推送到上。一旦满足子对象的右括号,我们将弹出堆栈的父对象,并将子对象添加到其集合中
正如前面提到的,一个好的解析器也应该涵盖一些特殊情况,我已经把实现这些情况的乐趣留给了读者。为了测试解析器,我在下面设置了控制台项目
namespace MySimpleParser
{
class Program
{
public static void Main(string[] args)
{
string s = GetInput();
try
{
Node root = Parser.Deserialize(s);
PrintBranch(root, 1);
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
Console.ReadLine();
}
internal static string GetInput()
{
return @"id i {
any data; any [con=tent]
id j {
any inner data
}
id k {
bla
id m {
any
}
thing
}
}";
}
internal static void PrintNode(Node n, int depth)
{
string indent = new string('-', 3 * depth);
Console.WriteLine($"{indent} Name: {n.Name}");
Console.WriteLine($"{indent} Contents: {n.Contents}");
Console.WriteLine($"{indent} Child Nodes: {n.InnerNodes.Count}");
}
internal static void PrintBranch(Node root, int depth)
{
PrintNode(root, depth);
foreach (Node child in root.InnerNodes) PrintBranch(child, depth + 1);
}
}
}
输出
--- Name: i
--- Contents: any data; any [con=tent]
--- Child Nodes: 2
------ Name: j
------ Contents: any inner data
------ Child Nodes: 0
------ Name: k
------ Contents: bla thing
------ Child Nodes: 1
--------- Name: m
--------- Contents: any
--------- Child Nodes: 0
不是解决这个问题最有效的方法,也不是最干净、最全面的方法。。。但可能是更短的 这使用正则表达式堆栈(我认为只有.Net正则表达式引擎才可用)
void Main()
{
变量输入=@“id i{
任何数据;任何[内容]
id j{
任何内部数据
}
身份证{
布拉
id m{
任何
}
事情}
}";
进程(输入).Dump();
}
公共类节点{
公共列表节点;
公共字符串内容;
公共字符串名称;
}
正则表达式reg=新正则表达式(@“
id\s+(?\w+)\s*\{(?)
(?>(?)\{{124;\}(?){(深度)[^\{\}]*})*
)\}(?)(?(深度)(?!)
“,RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
列表进程(字符串体){
返回注册表匹配项(正文)
.Cast()
.选择(x=>新节点{
Name=x.Groups[“Name”].值,
Contents=x.Groups[“body”].Value.Trim(),
InnerNodes=进程(x.Groups[“body”].Value),
}).ToList();
}
哪个输出
(注意:
.Dump()
是只转储生成对象的Linqpad扩展)使用递归算法。编写解析器。。。。再想想,你为什么要用这个?使用JSON或YAML。它们的解析器已经编写并可用这里的语法不仅仅是id NAME{CONTENT}
。您需要提供如何解析此数据的完整详细信息。@DuckQueen-我可以清楚地看到,如果有id
,则有名称
和{
,然后是内容
,但内容
包含其他id
以及其他类似“任何数据;任何[con=tent]“
,“bla”
,和“thing”
。您的类表明您需要一个嵌套结构,但您的语法没有解释如何解析其他内容以提取嵌套节点。您需要提供正确的语法。@DuckQueen-“仅原始嵌套结构”-这是问题所在。它不是“原始”-它包含一些id NAME{CONTENT}
和一些其他东西。所有这些都需要解析。你不能只解析一些而不解析其余的。
namespace MySimpleParser
{
class Program
{
public static void Main(string[] args)
{
string s = GetInput();
try
{
Node root = Parser.Deserialize(s);
PrintBranch(root, 1);
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
Console.ReadLine();
}
internal static string GetInput()
{
return @"id i {
any data; any [con=tent]
id j {
any inner data
}
id k {
bla
id m {
any
}
thing
}
}";
}
internal static void PrintNode(Node n, int depth)
{
string indent = new string('-', 3 * depth);
Console.WriteLine($"{indent} Name: {n.Name}");
Console.WriteLine($"{indent} Contents: {n.Contents}");
Console.WriteLine($"{indent} Child Nodes: {n.InnerNodes.Count}");
}
internal static void PrintBranch(Node root, int depth)
{
PrintNode(root, depth);
foreach (Node child in root.InnerNodes) PrintBranch(child, depth + 1);
}
}
}
--- Name: i
--- Contents: any data; any [con=tent]
--- Child Nodes: 2
------ Name: j
------ Contents: any inner data
------ Child Nodes: 0
------ Name: k
------ Contents: bla thing
------ Child Nodes: 1
--------- Name: m
--------- Contents: any
--------- Child Nodes: 0
void Main()
{
var input = @"id i {
any data; any [con=tent]
id j {
any inner data
}
id k {
bla
id m {
any
}
thing }
}";
Process(input).Dump();
}
public class Node {
public List<Node> InnerNodes;
public string Contents;
public string Name;
}
Regex reg = new Regex(@"
id\s+(?<name>\w+)\s*\{(?<body>(?<DEPTH>)
(?>(?<DEPTH>)\{ | \}(?<-DEPTH>) | (?(DEPTH)[^\{\}]* | ) )*
)\}(?<-DEPTH>)(?(DEPTH)(?!))
", RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
List<Node> Process(string body){
return reg.Matches(body)
.Cast<Match>()
.Select(x=>new Node{
Name=x.Groups["name"].Value,
Contents=x.Groups["body"].Value.Trim(),
InnerNodes=Process(x.Groups["body"].Value),
}).ToList();
}