C# 复杂字符串拆分

C# 复杂字符串拆分,c#,regex,string,parsing,split,C#,Regex,String,Parsing,Split,我有一个字符串,如下所示: [Testing.User]|Info:([Testing.Info]|Name:([System.String]|Matt)|Age:([System.Int32]|21))|Description:([System.String]|This is some description) 您可以将其视为以下树: - [Testing.User] - Info - [Testing.Info] - Name

我有一个字符串,如下所示:

[Testing.User]|Info:([Testing.Info]|Name:([System.String]|Matt)|Age:([System.Int32]|21))|Description:([System.String]|This is some description)
您可以将其视为以下树:

- [Testing.User]
- Info
        - [Testing.Info]
        - Name
                - [System.String]
                - Matt
        - Age
                - [System.Int32]
                - 21
- Description
        - [System.String]
        - This is some description
如您所见,它是类
测试的字符串序列化/表示。User

我希望能够进行拆分,并在生成的数组中获取以下元素:

 [0] = [Testing.User]
 [1] = Info:([Testing.Info]|Name:([System.String]|Matt)|Age:([System.Int32]|21))
 [2] = Description:([System.String]|This is some description)
我不能按
|
拆分,因为这样会导致:

 [0] = [Testing.User]
 [1] = Info:([Testing.Info]
 [2] = Name:([System.String]
 [3] = Matt)
 [4] = Age:([System.Int32]
 [5] = 21))
 [6] = Description:([System.String]
 [7] = This is some description)
我怎样才能得到预期的结果


我对正则表达式不是很在行,但我知道这是一个非常可能的解决方案。

这不是一个好的/健壮的解决方案,但如果您知道三个顶级项已修复,则可以将它们硬编码到正则表达式中

(\[Testing\.User\])\|(Info:.*)\|(Description:.*)
正如您所期望的那样,此正则表达式将创建一个包含三个组的匹配项。您可以在此处进行测试:

编辑:下面是一个完整的C#示例

使用系统;
使用System.Text.RegularExpressions;
命名空间控制台应用程序3
{
内部课程计划
{
私有静态void Main(字符串[]args)
{
const string input=@“[Testing.User]| Info:([Testing.Info]| Name:([System.string]| Matt)| Age:([System.Int32]| 21))| Description:([System.string]|这是一些描述)”;
常量字符串模式=@“(\[Testing\.User\])\\\\(信息:.*)\\\(说明:.*)”;
var match=Regex.match(输入,模式);
如果(匹配成功)
{
对于(int i=1;i
假设您的组可以标记为

  • [任何事,任何事]
  • 任何内容:第一个管道之后的任何内容(仅限字母和数字:然后是任意数量的字符)
  • 任何内容:在最后一个管道之后的任何内容(仅限字母和数字:然后是任何数量的字符)
  • 那么你就有了这样的模式:

    "(\\[\\w+\\.\\w+\\])\\|(\\w+:.+)\\|(\\w+:.+)";
    
    • (\\[\\w+\.\\w+\\])
      此捕获组将获得“[Testing.User]”,但不限于“[Testing.User]”
    • \\\\\;(\\w+:.+)
      此捕获组将在第一个管道之后获取数据,并在最后一个管道之前停止。在本例中,“Info:([Testing.Info]| Name:([System.String]| Matt)| Age:([System.Int32]| 21)),但不限于以“Info:”开头的信息:
    • \\\\\\\(\\w+:.+)
      与前面的捕获组相同,但捕获最后一个管道之后的内容,在本例中为“描述:([System.String]|这是一些描述)”,但不限于以描述开头:
    现在,如果要添加另一个管道,后面跟着更多数据(
    | Anything:SomeData
    ),则
    说明:
    将是组2的一部分,组3现在将是“
    Anything:SomeData

    代码如下所示:

    using System;
    using System.Text.RegularExpressions;
    
    public class Program
    {
        public static void Main()
        {
            String text = "[Testing.User]|Info:([Testing.Info]|Name:([System.String]|Matt)|Age:([System.Int32]|21))|Description:([System.String]|This is some description)";
            String pattern = "(\\[\\w+\\.\\w+\\])\\|(\\w+:.+)\\|(\\w+:.+)";
    
            Match match = Regex.Match(text, pattern);
            if (match.Success)
            {
                Console.WriteLine(match.Groups[1]);
                Console.WriteLine(match.Groups[2]);
                Console.WriteLine(match.Groups[3]); 
            }
        }
    }
    
    结果:

    [Testing.User]
    Info:([Testing.Info]|Name:([System.String]|Matt)|Age:([System.Int32]|21))
    Description:([System.String]|This is some description)
    
    请参阅此处的工作示例


    如果我在这里添加另一个遵循模式格式的字段,请参见工作示例…

    要做到这一点,您需要使用.net正则表达式引擎独有的正则表达式功能。这是一个计数器系统,当发现一个左括号时,计数器递增,当发现一个右括号时,计数器递减,然后您只需测试计数器为null以了解括号是否平衡。 这是确保您在括号内或括号外的唯一方法:

    using System;
    using System.Text.RegularExpressions;
    
    public class Example
    {
       public static void Main()
       {
           string input = @"[Testing.User]|Info:([Testing.Info]|Name:([System.String]|Matt)|Age:([System.Int32]|21))|Description:([System.String]|This is some description)";
    
           string pattern = @"(?:[^|()]+|\((?>[^()]+|(?<Open>[(])|(?<-Open>[)]))*(?(Open)(?!))\))+";
    
           foreach (Match m in Regex.Matches(input, pattern)) 
               Console.WriteLine(m.Value);
       }
    }
    
    使用系统;
    使用System.Text.RegularExpressions;
    公开课范例
    {
    公共静态void Main()
    {
    字符串输入=@“[Testing.User]| Info:([Testing.Info]| Name:([System.string]| Matt)|年龄:([System.Int32]| 21))|说明:([System.string]|这是一些说明)”;
    字符串模式=@“(?:[^ |()]+|\(?>[^()]+|)(?[())(?[)))*(?(打开)(?!)\)+”;
    foreach(在正则表达式中匹配m.Matches(输入,模式))
    控制台写入线(m值);
    }
    }
    

    图案详情:

    (?:
        [^|()]+    # all that is not a parenthesis or a pipe
      |            # OR
                   # content between parenthesis (eventually nested)
        \(              # opening parenthesis
         # here is the way to obtain balanced parens
        (?> # content between parens
            [^()]+        # all that is not parenthesis 
          |               # OR
            (?<Open>[(])  # an opening parenthesis (increment the counter)
          |
            (?<-Open>[)]) # a closing parenthesis (decrement the counter)
        )*  # repeat as needed
        (?(Open)(?!)) # make the pattern fail if the counter is not zero
    
        \)
    )+
    
    (?)
    [^ |()]+#所有这些都不是括号或管道
    |#或
    #括号之间的内容(最终嵌套)
    \(#左括号)
    #下面是获得平衡参数的方法
    (?>#双方之间的内容
    [^()]+#所有这些都不是括号
    |#或
    (?[(])#一个圆括号(递增计数器)
    |
    (?[)]#右括号(减少计数器)
    )*#根据需要重复
    (?(打开)(?!)#如果计数器不为零,则使模式失败
    \)
    )+
    
    (?(open)(?!)
    是一个条件语句

    (?!)
    是一个始终为假的子模式(一个空的负前瞻),意思是:后面不跟零

    此模式匹配所有非管道和括号之间的字符串。

    使用regex lookahead 您可以使用这样的正则表达式:

    (\[.*?])|(\w+:.*?)\|(?=Description:)|(Description:.*)
    
    (\[.*?])\|(.*)\|(Description:.*)
    

    这个正则表达式背后的思想是按组捕获您想要的内容
    1
    2
    3

    通过此图,您可以很容易地看到:

    匹配信息

    MATCH 1
    1.  [0-14]   `[Testing.User]`
    MATCH 2
    2.  [15-88]  `Info:([Testing.Info]|Name:([System.String]|Matt)|Age:([System.Int32]|21))`
    MATCH 3
    3.  [89-143] `Description:([System.String]|This is some description)`
    
    正则正则表达式 另一方面,如果您不喜欢上面的正则表达式,可以使用另一个这样的正则表达式:

    (\[.*?])|(\w+:.*?)\|(?=Description:)|(Description:.*)
    
    (\[.*?])\|(.*)\|(Description:.*)
    

    甚至强制至少一个字符:

    (\[.+?])\|(.+)\|(Description:.+)
    

    正则表达式并不是解决此类问题的最佳方法,您可能需要编写一些代码来解析数据,我做了一个简单的示例,实现了您的这种简单情况。这里的基本思想是,只有当
    不在括号内时,才需要拆分,因此我会跟踪括号计数。您需要做一些操作例如,在威胁案例中,括号是描述部分的一部分,但正如我所说,这只是一个起点:

    static IEnumerable<String> splitSpecial(string input)
    {
        StringBuilder builder = new StringBuilder();
        int openParenthesisCount = 0;
    
        foreach (char c in input)
        {
            if (openParenthesisCount == 0 && c == '|')
            {
                yield return builder.ToString();
                builder.Clear();
            }
            else
            {
                if (c == '(')
                    openParenthesisCount++;
                if (c == ')')
                    openParenthesisCount--;
                builder.Append(c);
            }
        }
        yield return builder.ToString();
    }
    
    static void Main(string[] args)
    {
        string input = "[Testing.User]|Info:([Testing.Info]|Name:([System.String]|Matt)|Age:([System.Int32]|21))|Description:([System.String]|This is some description)";
        foreach (String split in splitSpecial(input))
        {
            Console.WriteLine(split);
        }
        Console.ReadLine();
    }
    

    已经有足够多的拆分答案了,所以这里有另一种方法。如果您的输入表示一个树结构,为什么不将其解析为一个树呢? 以下代码是从VB.NET自动翻译的,但是
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Treeparse
    {
        class Program
        {
            static void Main(string[] args)
            {
                var input = "[Testing.User]|Info:([Testing.Info]|Name:([System.String]|Matt)|Age:([System.Int32]|21))|Description:([System.String]|This is some description)";
                var t = StringTree.Parse(input);
                Console.WriteLine(t.ToString());
                Console.ReadKey();
            }
        }
    
        public class StringTree
        {
            //Branching constants
            const string BranchOff = "(";
            const string BranchBack = ")";
            const string NextTwig = "|";
    
            //Content of this twig
            public string Text;
            //List of Sub-Twigs
            public List<StringTree> Twigs;
            [System.Diagnostics.DebuggerStepThrough()]
            public StringTree()
            {
                Text = "";
                Twigs = new List<StringTree>();
            }
    
            private static void ParseRecursive(StringTree Tree, string InputStr, ref int Position)
            {
                do {
                    StringTree NewTwig = new StringTree();
                    do {
                        NewTwig.Text = NewTwig.Text + InputStr[Position];
                        Position += 1;
                    } while (!(Position == InputStr.Length || (new String[] { BranchBack, BranchOff, NextTwig }.ToList().Contains(InputStr[Position].ToString()))));
                    Tree.Twigs.Add(NewTwig);
                    if (Position < InputStr.Length && InputStr[Position].ToString() == BranchOff) { Position += 1; ParseRecursive(NewTwig, InputStr, ref Position); Position += 1; }
                    if (Position < InputStr.Length && InputStr[Position].ToString() == BranchBack)
                        break; // TODO: might not be correct. Was : Exit Do
                    Position += 1;
                } while (!(Position >= InputStr.Length || InputStr[Position].ToString() == BranchBack));
            }
    
            /// <summary>
            /// Call this to parse the input into a StringTree objects using recursion
            /// </summary>
            public static StringTree Parse(string Input)
            {
                StringTree t = new StringTree();
                t.Text = "Root";
                int Start = 0;
                ParseRecursive(t, Input, ref Start);
                return t;
            }
    
            private void ToStringRecursive(ref StringBuilder sb, StringTree tree, int Level)
            {
                for (int i = 1; i <= Level; i++)
                {
                    sb.Append("   ");
                }
                sb.AppendLine(tree.Text);
                int NextLevel = Level + 1;
                foreach (StringTree NextTree in tree.Twigs)
                {
                    ToStringRecursive(ref sb, NextTree, NextLevel);
                }
            }
    
            public override string ToString()
            {
                var sb = new System.Text.StringBuilder();
                ToStringRecursive(ref sb, this, 0);
                return sb.ToString();
            }
    
        }
    }