如何使用Regex.Split

时间:2019-07-05 09:28:12

标签: c# regex

我正在处理可能包含括号之间的内容的字符串,例如:

"Hello World"
"(Hello) World"
"(Hello World)"
"(Hello) (World)"
"bla bla (Hello World) bla bla"
"Hello (World"

我为此编写了一个简单的正则表达式:\((.*?)\)

var Inputs = new List<string>
{
    "Hello World",
    "(Hello) World",
    "(Hello World)",
    "(Hello) (World)",
    "bla bla (Hello World) bla bla",
    "Hello (World"
};

foreach (var input in Inputs)
{
    var parts = Regex.Split(input, @"\((.*?)\)");

    Console.WriteLine($"Input : {input}");
    foreach (var part in parts)
    {
        Console.WriteLine($"> '{part}'");
    }
    Console.WriteLine("--------------------------------");
}

这给了我预期的输出:

Input : Hello World
> 'Hello World'
--------------------------------
Input : (Hello) World
> ''
> 'Hello'
> ' World'
--------------------------------
Input : (Hello World)
> ''
> 'Hello World'
> ''
--------------------------------
Input : (Hello) (World)
> ''
> 'Hello'
> ' '
> 'World'
> ''
--------------------------------
Input : bla bla (Hello World) bla bla
> 'bla bla '
> 'Hello World'
> ' bla bla'
--------------------------------
Input : Hello (World
> 'Hello (World'
--------------------------------

但是我需要对括号之间的捕获部分进行特殊处理。

我以为使用了命名组,例如(?<others1>.*?)\((?<choice>.*?)\)(?<others2>.*?),但是与组一起工作需要使用Match()GetGroupNames()这样的方法,但结果却是错误的:

// Inputs are the same than above
foreach (var input in Inputs)
{        
    var rgx = new Regex(@"(?<others1>.*?)\((?<choice>.*?)\)(?<others2>.*?)");

    var matches = rgx.Matches(input);
    var groups = rgx.GetGroupNames();


    Console.WriteLine($"Input : {input}");
    foreach (Match match in matches)
    {
        foreach (var group in groups)
        {
            Group grp = match.Groups[group];
            Console.WriteLine("   {0}: '{1}'", group, grp.Value);
            // if (group == "choice")
            //     SpecialTreatment(grp.Value);
        }
    }
    Console.WriteLine("--------------------------------");
}

输出:

Input : Hello World // no match
--------------------------------
Input : (Hello) World // Missing ' World'
   0: '(Hello)'
   others1: ''
   choice: 'Hello'
   others2: ''
--------------------------------
Input : (Hello World) // Good
   0: '(Hello World)'
   others1: ''
   choice: 'Hello World'
   others2: ''
--------------------------------
Input : (Hello) (World) // Good
   0: '(Hello)'
   others1: ''
   choice: 'Hello'
   others2: ''
   0: ' (World)'
   others1: ' '
   choice: 'World'
   others2: ''
--------------------------------
Input : bla bla (Hello World) bla bla // missing last part ' bla bla'
   0: 'bla bla (Hello World)'
   others1: 'bla bla '
   choice: 'Hello World'
   others2: ''
--------------------------------
Input : Hello (World // no match
--------------------------------

是否可以使用Regex.Split()使组名受益?

2 个答案:

答案 0 :(得分:0)

坦率地说,这个问题遍及整个地图。您是否想要最终结果,或者想要解决此过程中的某个步骤?成为程序员的一部分是将事情分解成小步并在每个步骤上进行工作。您还没有这样做...所以让我们这样做:

  1. 匹配非分组文本或分组文本。
  2. 如果将其分组,则将每个单独的项目分离到一个列表中。
  3. 如果未分组,则照原样进行。
  4. 如果是组合方案,则重新组合时,请从列表中选择比上次操作+1索引的项。

那么您对哪个有疑问?提出SO问题时,只需将其咬成一小块即可。


看着bulk-email-generator problem,可以使用正则表达式来解决该问题,但是必须使用匹配项中找到的捕获组将项目正确分离。一旦它们分开,您就可以采用目标项目,该项目是在每次比赛后都会增加的索引。

示例

xxx (abc|def|ghi) yyy (ijk|lmn|opq) zzz

最终结果

 xxx abc yyy lmn zzz

模式 这是模式,注释后进行解释。此正则表达式查找 OR 非组。从这些组中,它将单独的文本添加到内部捕获数组中:

var pattern = @"          # Either its in a Group
 \(                            #(literal paren start)
     (
       (?<Grouped>[^|(]+)      # Match up to the pipe or paren
       \|?                     # Don't match the pipe but consume it if there
    )+                         # One to many of these piped items
 \)                               # (literal paren stop)
|                          # Or its not in a group
 (?<NotGrouped>[^(]+)          #
";

请注意,我们将告诉regex解析器使用RegexOptions.IgnorePatternWhitespace(允许我们在regex解析之前之前注释模式,还使用RegexOptions.ExplicitCapture来注释正则表达式解析器中的任何内容)命名匹配捕获(?<NameHere> )

正则表达式结果

Match #0
                   [0]:  xxx
     ["Grouped"] → [1]:
  ["NotGrouped"] → [2]:  xxx
           →2 Captures: xxx
Match #1
                   [0]:  (abc | def | ghi)
     ["Grouped"] → [1]:  ghi
           →1 Captures: abc, def, ghi
 ["NotGrouped"] → [2]:
Match #2
                   [0]:   yyy
     ["Grouped"] → [1]:
  ["NotGrouped"] → [2]:   yyy
           →2 Captures: yyy
Match #3
                   [0]:  (ijk | lmn | opq)
     ["Grouped"] → [1]:  opq
           →1 Captures: ijk, lmn, opq
 ["NotGrouped"] → [2]:
Match #4
                   [0]:   zzz
     ["Grouped"] → [1]:
  ["NotGrouped"] → [2]:   zzz
           →2 Captures: zzz

因此,如果分组为NonPiped,我们将忽略“捕获”(只有一个)与火柴匹配的棍子。如果它是数据组之一,我们将重点放在Captures上。

C#解决方案

int index = 0;

string.Join(string.Empty,

Regex.Matches(text, pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture)
     .OfType<Match>()
     .Select(mtch => mtch.Groups["NotGrouped"].Success ? mtch.Groups["NotGrouped"].Value
                                                       : mtch.Groups["Grouped"].Captures
                                                                               .OfType<Capture>()
                                                                               .Select(cpt => cpt.Value)
                                                                               .ToList()[index++]
             )

    )

结果是xxx abc yyy lmn zzz

答案 1 :(得分:-1)

您可以使用稍有不同的正则表达式来保留括号:

var parts = Regex.Split(input, @"(\(.*?\))");

问题中的一个示例应报告:

Input : (Hello) World
> ''
> '(Hello)'
> ' World'

然后只需检查parts的每个元素的开头和结尾字符是否包含方括号。


另一种方法将使用:

var parts = Regex.Split(input, @"([()])");

这应该产生:

Input : (Hello) World
> ''
> '('
> 'Hello'
> ')'
> ' World'

这也清楚地显示了方括号内的文本。