为什么这个.net regex在自由空间模式下失败?



我正在尝试使用我在自由空间模式下制作的正则表达式,因此我可以添加注释并保持其可维护的未来。下面的程序是这个问题的一个示例,但是我遇到这个问题的实际正则表达式要复杂得多。

private static readonly string regexTest1 = @"(?((?=A))(A)|(w))";
private static readonly string regexTest2 = @"(?x)
(?        # If ...
((?=A))   # next character is A
(A)|      # Capture in group 1, else ...
(w))     # Capture in group 2 (and EndIf).
";
static void Main(string[] args)
{
Match m1 = new Regex(regexTest1).Match("A");
Match m2 = new Regex(regexTest2).Match("A");  // Exception!
}

尝试使用regexTest2时,程序出现"无法识别的令牌"中断。例外。似乎是条件句"(?’和再多三行的结束括号才是问题的根源,但为什么不能把它们分成几行呢?

作为一种工作,我使用像这样的连接字符串…

private static readonly string regexTest2 =
@"(?"          // If ...
+@"((?=A))"    // next character is A
+@"(A)|"       // Capture in group 1, else ...
+@"(w))";     // Capture in group 2 (and EndIf).

但是我发现了@和"过多地分散了对正则表达式部分的注意力。在。net中以这种方式分解正则表达式有什么限制?有没有别的(更清晰的)方式被我忽略了?

看起来(?在它自己的行上会导致解析器出现问题。我不知道为什么,但是很容易解决。我个人会使用显式RegexOptions而不是(?x),但两者都有效:

using System.Text.RegularExpressions;
string pattern = @"
(?((?=A)) # If next character is A
(A)|      # Capture in group 1, else ...
(w))     # Capture in group 2 (and EndIf).
";
Match match = new Regex(pattern, RegexOptions.IgnorePatternWhitespace).Match("A");

或:

string pattern =
@"(?x)    # Ignore pattern whitespace
(?((?=A)) # If next character is A
(A)|      # Capture in group 1, else ...
(w))     # Capture in group 2 (and EndIf).
";
Match match = new Regex(pattern).Match("A");

请注意,虽然这样写更容易读:

string pattern = @"
(?x) # Ignore pattern whitespace
...
";

…由于选项前的空白(换行),这将不起作用。它不会抛出异常(它是一个有效的正则表达式),但它不会像你希望的那样匹配。

如果你一定要把最外层的分组结构和第一个分开,你可以给它一个名字:

using System.Text.RegularExpressions;
string pattern = @"
(?'outer' # If...
((?=A))   # next character is A
(A)|      # Capture in group 1, else ...
(w))     # Capture in group 2 (and EndIf).
";
Match match = new Regex(pattern, RegexOptions.IgnorePatternWhitespace).Match("A");