我可以在不抛出异常的情况下测试正则表达式在 C# 中是不是有效吗
Posted
技术标签:
【中文标题】我可以在不抛出异常的情况下测试正则表达式在 C# 中是不是有效吗【英文标题】:Can I test if a regex is valid in C# without throwing exception我可以在不抛出异常的情况下测试正则表达式在 C# 中是否有效吗 【发布时间】:2010-09-18 03:14:36 【问题描述】:我允许用户输入正则表达式来匹配 IP 地址,以便在相关系统中进行 IP 过滤。我想验证输入的正则表达式是否有效,因为很多用户会搞砸操作,虽然是出于好意。
我当然可以在 try/catch 中执行 Regex.IsMatch() 并查看它是否会以这种方式爆炸,但是有没有更聪明的方法呢?速度本身不是问题,我只是希望避免无缘无故抛出异常。
【问题讨论】:
我有一种方法可以测试正则表达式是否有效,但它只是将正则表达式包装在 Try/Catch 中。我不确定是否有更好的方法来做到这一点,但我找不到。 你的意思是对创建实际的正则表达式大发雷霆吗?新的正则表达式(str)? 允许用户输入每个八位字节的开始和结束值(或类似的解决方案)可能值得考虑而不是正则表达式。 如果您的 IP 地址正则表达式用于范围,您也可以考虑使用 CIDR (192.168.0.0/24)。 en.wikipedia.org/wiki/CIDR 【参考方案1】:我认为在这种情况下例外是可以的。
只要确保短路并消除你可以的异常:
private static bool IsValidRegex(string pattern)
if (string.IsNullOrWhiteSpace(pattern)) return false;
try
Regex.Match("", pattern);
catch (ArgumentException)
return false;
return true;
【讨论】:
我想知道 JIT 编译器是否足够聪明或足够愚蠢以优化整个 try catch 块,因为没有使用纯函数的返回值?IsMatch()
会比Match()
更快/更好吗,因为我们实际上并不想进行匹配?就像测试素数比实际找到因子要快得多(嗯,几乎)。
IsMatch()
calls internal Match Run(bool quick, int prevlen, string input, int beginning, int length, int startat)
quick
设置为 true
而 Match()
调用它时 quick
设置为 false
。根据我的简单测试,确实快一点,大约 1-5%。
new Regex(pattern)
怎么样?
问题专门询问是否可以在不处理异常的情况下完成。【参考方案2】:
只要您捕获到非常具体的异常,只需执行 try/catch。
如果使用得当,异常并不邪恶。
【讨论】:
该问题专门询问是否可以在不处理异常的情况下完成。Exceptions are not evil if used correctly
但它们的抛出成本很高,因为它们在其中包含堆栈跟踪的转储。异常应该用于实际错误(理想情况下),而不是用于测试输入
but they are expensive to throw
我刚刚花了一天时间尝试将 MS RegexParser.ScanRegex()
转换为只返回一个表示错误的 enum
的东西。我敢打赌,虽然抛出异常可能会耗费大量内存,但添加一堆检查会带来巨大的性能成本。如果我完成它,我会对其进行基准测试。
我想我会吃掉我的帽子github.com/mwagnerEE/BenchmarkResults/blob/main/…【参考方案3】:
并非没有大量工作。正则表达式解析可能非常复杂,并且框架中没有任何公开的内容来验证表达式。
System.Text.RegularExpressions.RegexNode.ScanRegex()
看起来是负责解析表达式的主要函数,但它是内部函数(无论如何都会抛出任何无效语法的异常)。因此,您需要重新实现解析功能——这无疑会在边缘情况或框架更新时失败。
我认为在这种情况下捕获 ArgumentException 是一个好主意。
【讨论】:
我们有 TryParse 来处理可能格式错误的数字。我们应该有一个 TryRegex 来做同样的事情——返回失败而不是异常。调试用户输入的正则表达式很烦人! 我认为它实际上是System.Text.RegularExpressions.RegexParser.ScanRegex()
来源:referencesource.microsoft.com/#System/regex/system/text/…【参考方案4】:
我曾经使用过下面的功能并且对此没有任何问题。它同时使用异常和超时,但它是功能性的。 当然,它适用于 .Net Framework >= 4.5。
public static bool IsValidRegexPattern(string pattern, string testText = "", int maxSecondTimeOut = 20)
if (string.IsNullOrEmpty(pattern)) return false;
Regex re = new Regex(pattern, RegexOptions.None, new TimeSpan(0, 0, maxSecondTimeOut));
try re.IsMatch(testText);
catch return false; //ArgumentException or RegexMatchTimeoutException
return true;
【讨论】:
请更改时间跨度,它应该是 TimeSpan(0,0,maxSecondsTime...) 而不是硬编码的“20” 我现在改了。非常感谢我的朋友。【参考方案5】:格式错误的正则表达式并不是导致异常的最坏原因。
除非你接受一个非常有限的正则表达式语法子集——然后为此编写一个正则表达式(或解析器)——我认为你没有其他方法可以测试它是否有效,只能尝试从它构建一个状态机并让它匹配一些东西。
【讨论】:
【参考方案6】:这取决于目标是谁,我会非常小心。构建可以自行回溯并占用大量 CPU 和内存的正则表达式并不难——它们可以成为有效的拒绝服务向量。
【讨论】:
.NET 正则表达式解析库中是否没有“堆栈溢出”保护?你能给我一个可能会给我带来麻烦的例子吗? Regex.IsMatch("bbbbbbbbbb", "(.*)50a"); 使用 .NET 4.5,您可以为 Regex 对象添加超时值【参考方案7】:在 .NET 中,除非您编写自己的正则表达式解析器(我强烈建议您不要这样做),否则您几乎可以肯定需要使用 try/catch 来包装新 Regex 对象的创建。
【讨论】:
【参考方案8】:通过使用以下方法,您可以检查您的正则表达式是否有效。这里 testPattern 是你必须检查的模式。
public static bool VerifyRegEx(string testPattern)
bool isValid = true;
if ((testPattern != null) && (testPattern.Trim().Length > 0))
try
Regex.Match("", testPattern);
catch (ArgumentException)
// BAD PATTERN: Syntax error
isValid = false;
else
//BAD PATTERN: Pattern is null or blank
isValid = false;
return (isValid);
【讨论】:
标题特意写着“不抛出异常”! 修剪模式也是不好的形式,因为它可能包含用于匹配的空格以上是关于我可以在不抛出异常的情况下测试正则表达式在 C# 中是不是有效吗的主要内容,如果未能解决你的问题,请参考以下文章