Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从C#中的字符串解析嵌套结构?_C#_.net_String_Parsing_Tree - Fatal编程技术网

如何从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解析此类对象树?

您有几个选项:

  • 学习编写文档类型的解析器。有很多这样的在线资源可以帮助你做到这一点。如果你想走这条路,我建议你看一下字符串部分,因为它们中的许多有助于教授这种操作。虽然这是迄今为止最困难的一条路,但如果你学会了这条路,结果会更好

  • 使用图书馆。对于C#,有许多不同的解析器库,每个都有各自的优缺点。目前我个人最喜欢的是,它似乎是最简单的一元语法分析器库,只需选择和使用它,而不必了解函数编程或一般的语法分析

  • 如果我多了解一点情况,我就能提供更好的指导,但是你提供的信息非常少。有用的信息:

    • 尺寸/速度要求
    • 文档结构的更精确定义
    这似乎类似于另一本教科书中关于检查偏执是否平衡的问题。基本上是取一个堆栈,解析字符串,当你遇到左括号时。。。了解正在创建的对象。右括号有助于将物体标记为完整。

    @T.S.在评论中指出,除了教育目的外,很少有好的理由重新发明轮子。当谈到编写解析器时,正是这些极端情况让我们很难做出正确的选择:

    • 格式验证,更具体地说是返回友好 发送给用户的消息
    • 优化代码的性能、分配、并发性
    也就是说,有了C#7对模式匹配的支持,基本管道应该很容易实现

    第一站,节点类,添加初始化

    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();
    }