C# 为什么这个正则表达式有两个后斜杠,但只有一个可以正常工作?

C# 为什么这个正则表达式有两个后斜杠,但只有一个可以正常工作?,c#,.net,regex,C#,.net,Regex,我使用regex编写了以下代码,该代码在运行时挂起在最后一行: const char PathSeparator = '\\'; const string PathPrefix = "\\\\"; var reg = new Regex(string.Format("^{0}(([^{1}\r\n\v\f\u2028\u2029]+){1}?)+$", Regex.Escape(PathPrefix), Regex.Escape(PathSeparator.ToString())))

我使用regex编写了以下代码,该代码在运行时挂起在最后一行:

const char PathSeparator = '\\';
const string PathPrefix = "\\\\";

var reg = new Regex(string.Format("^{0}(([^{1}\r\n\v\f\u2028\u2029]+){1}?)+$", 
    Regex.Escape(PathPrefix), Regex.Escape(PathSeparator.ToString())));

var test = @"\\Inbox\Test123\Intermediate\3322FB69-FE3F-407E-9E15-584382A407A2\\";
var matches = reg.Matches(test);
var group = matches[0].Groups[2];
如果我删除
测试中的最后一个反斜杠,它就可以正常工作,例如

var test = @"\\Inbox\Test123\Intermediate\3322FB69-FE3F-407E-9E15-584382A407A2\";
有人能帮我弄清楚它为什么挂着吗?我知道我可以设置一个超时;这不是我的问题。

现在你有两个问题 在回答这个问题时,我必须首先提到众所周知的问题。虽然regex是一个强大的工具,可以有效地解决很多问题(包括这一个),但如果你不是这方面的专家,它也可能是一个很大的麻烦来源,并造成痛苦,而采用低技术解决方案完全可以避免

在本例中,您可以通过在斜杠上拆分输入字符串,然后验证自己生成的每个标记(是的,甚至可能使用正则表达式)来完成同样的工作。这实际上可能是一个明智的举动,因为这将大大降低解决方案的复杂性;当需要“一个小的调整”(增加复杂性)时,这将是非常有价值的

也就是说,让我们看看有趣的事情:发生了什么

为什么它永远运行? 由于嵌套的
+
量词导致。这将更容易说明使用更温和的输入样本

\\Foo\Bar\Baz\\
让我们看看当输入时正则表达式引擎将尝试匹配什么

1) 第一次尝试:最里面的组匹配
Foo\
Bar\
Baz\
。由于尾随的
\
,进一步重复失败,然后输入结束标记无法匹配,因此尝试被拒绝,引擎返回

注意,如果尾斜杠不在那里,正则表达式引擎将声明成功并在此时返回

2) 由于最内层组中的斜杠是可选的,因此下一次尝试是
Foo\
Bar\
Baz
,这显然也失败了。更多的回溯

3) 下一个候选项匹配最内层组的四个重复项:
Foo\
Bar\
Ba
z\
。失败,更多的回溯

从现在起,我将用一个空格分隔最内层组的重复,简单列出尝试的匹配:

Foo\ Bar\ Ba z      (i.e. 4 repetitions of length 4, 4, 2, 1, and fails)
Foo\ Bar\ Ba
Foo\ Bar\ B az\
Foo\ Bar\ B az
Foo\ Bar\ B a
Foo\ Bar\
等等

这里要注意的是,不合理的尝试甚至排除了落后的“BAZ”段可以成为成功匹配的一部分:我们必须考虑“BAZ”、“巴”+“Z”、“巴”、“B”+“AZ”、“B”+“A”和“B”;一般来说,对于长度为N的段,可能性的数量为N!(阶乘)。阶乘函数的增长超出了人类的直觉理解

由于示例输入包含长度为36的最后一段,很明显,尝试将此正则表达式与格式不正确的输入匹配将永远不会结束

如何防止这种情况发生? 在这种情况下,它非常简单。由于您知道,例如“Baz”,如果整体匹配失败,那么尝试将其分割为更小的块是没有意义的(因为这些块需要用斜线分隔,我们已经知道它们不是因为斜线不是尝试匹配的允许部分),请使用原子捕获组:

var reg = new Regex(string.Format(
    "^{0}(?>([^{1}\r\n\v\f\u2028\u2029]+{1}?))+$",
    Regex.Escape(PathPrefix),
    Regex.Escape(PathSeparator.ToString())));

这将只回溯1次而不是N!对于未成功匹配的每个路径段,将失败时间减少到几乎为零。

注意,我最初粘贴时犯了一个错误。最后一个反斜杠可以正常工作,但挂起两个。请看编辑。也许这是一个问题,但我真的不明白为什么会是这种情况。正是因为,过度回溯。问题是嵌套的
+
量词。如果您有此问题,请编辑您的标题,使其成为您搜索的内容。你会搜索“为什么这个正则表达式挂起”?标题对于搜索引擎优化非常重要,这样其他有你问题的人可以快速找到答案。作为一种解决方案,使用原子捕获组(
^\\\\(?>
..
)$
)工作起来很简单。还有其他结构也可以完成这项工作(例如,使路径分隔符成为无条件的,并用一个可选的[^…]组结束正则表达式)。感谢您的精彩解释。值得一提的是,我确实开始使用非正则表达式,但很快就发现我必须处理各种可能的“错误”输入——例如,路径前缀被省略,路径以3个或更多分隔符开头,路径内部包含连续分隔符,等等。如果我只使用分隔符拆分,在某种意义上,我必须处理我自己的表单a回溯,因为我在生成的数组中循环。你可以看到它是多么的混乱……regex似乎是(现在仍然是)获胜的方法。