我可以在不抛出异常的情况下测试正则表达式在 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 设置为 trueMatch() 调用它时 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# 中是不是有效吗的主要内容,如果未能解决你的问题,请参考以下文章

如何在不抛出 TaskCanceledExceptions 的情况下等待任务?

在不丢失堆栈跟踪的情况下重新抛出 Java 中的异常

JAXB XMLAdapter 方法不抛出异常

如何找出PHP代码块可能抛出的所有错误?

为啥 C# 编译器不抛出 null 的逻辑比较?

正则表达式匹配我的模式抛出异常