有人可以解释这个RegEx行为吗?

时间:2012-11-27 17:42:58

标签: c# .net regex

使用RegEx时遇到一种奇怪的行为。

dataString = "#Name #Location New York #Rating"
string[] rawValues = Regex.Split(dataString.Trim(), "(^|\\s)+#\\w+");

模式匹配:"#Name", " #Location", " #Rating"(这是我打算匹配的) 拆分返回:["", "", "", " ", "New York", " ", ""]

问题#1:cunfusion已经在这里开始了。为什么位置0,1,2上有空字符串?两个匹配,一个因为它位于字符串的第一个位置?

但这不是奇怪的部分。

string[] rawValues = Regex.Split(dataString.Trim(), "(\\s|^)+#(\\w*[A-Za-z_]+\\w*)");

模式匹配:"#Name", " #Location", " #Rating"(与之前相同) 但是分裂返回:["", "", "Name", "", " ", "Location"," New York", " ", "Rating",""]

问题#2:导致完全相同匹配的模式导致完全不同的分割输出。这怎么可能?

2 个答案:

答案 0 :(得分:2)

原因是这句话来自MSDN

  

如果在Regex.Split表达式中使用捕获括号,则任何捕获的文本都包含在结果字符串数组中。

如果您真的只想在匹配时拆分字符串,则不应在Split中使用捕获组。您可以使用(?:...)代替您拥有的每个(...)来避免捕获群组。

另外,正如你所正确的那样。第一个和最后一个""源于字符串以匹配开始和结束的事实(因此在分割中将报告这些匹配之前和之后的空字符串)。

这是一个更适合您的正则表达式:

@"(?:^|\s+)#\w*[A-Za-z_]+\w*"

请注意,在第一个子模式之外使用+也是不必要的,并导致尴尬的副作用。首先,它允许群组多次捕获(这就是为什么你有两个"""":一个用于^,一个用于\s)。其次,在第一个空格字符匹配后不需要重复^,因此仅重复空格字符就足够了。此外,根本不需要在#之后对单词进行分组。

但是,如果你想要的是匹配#name之类的东西,当它位于字符串的开头或者前面有一个空格(即 not *前面是**非空格字符),为什么在匹配中包含可能的空格。消极的外观为你提供了一个很好的出路:

@"(?<!\S)#\w*[A-Za-z_]+\w*"

这完全如上所述。 (?<!\S)匹配,如果没有剩余非空格字符(如果有匹配则不包括匹配中的空格字符)。这涵盖了两种情况,没有交替,您不需要Trim您的密钥名称。

答案 1 :(得分:0)

因为您要拆分的正则表达式匹配1个或更多空格,后跟哈希('#')后跟1个或多个单词字符。

任何匹配的内容都不会包含在结果中。

有两种方法可以做到这一点:

  1. 拆分不需要的内容并过滤结果。
  2. 主动搜索只需要的内容。
  3. 这里有一些代码包含上述两个选项:

    static void Main( string[] args )
    {
        string   sourceText = "#Name #Location New York #Rating" ;
    
        // option 1: split on whitespace and then toss whatever isn't wanted
        string[] hashTokens1 = sourceText.Split().Where( x => x.StartsWith("#") ).ToArray() ;
    
        // option 2: actively search for what is desired
        string[] hashTokens2 = ParseSourceData( sourceText ).ToArray() ;
    
        return ;
    
    }
    
    private static readonly Regex hashTokenPattern = new Regex( @"#\w+");
    private static IEnumerable<string> ParseSourceData( string s )
    {
        for ( Match m = hashTokenPattern.Match( s ) ; m.Success ; m = m.NextMatch() )
        {
            yield return m.Value ;
        }
    }
    

    我自己,我会使用第二个选项,因为它更能说明你想要完成的事情。一个好的一般规则是倾向于积极断言或测试而不是消极。

    您还可以将第二个选项写为“单行,因此:

    // option 2: actively search for what is desired
    Regex hashTokenPattern = new Regex( @"#\w+");
    string[] hashTokens2 = hashTokenPattern.Matches(sourceText).Cast<Match>().Select(x=>x.Value).ToArray();