gpt4 book ai didi

.net - 如何使正则表达式操作超时以防止在 .NET 4.5 中挂起?

转载 作者:行者123 更新时间:2023-12-02 00:51:46 25 4
gpt4 key购买 nike

有时能够限制正则表达式操作的模式匹配持续时间可能很有用。特别是,当使用用户提供的模式来匹配数据时,由于嵌套的量词和过多的回溯,该模式可能表现出较差的性能(参见 catastrophic backtracking)。应用超时的一种方法是异步运行正则表达式,但这可能很乏味并且会使代码困惑。
根据what's new in the .NET Framework 4.5 Developer Preview看起来有一种新的内置方法可以支持这一点:

Ability to limit how long the regular expression engine will attemptto resolve a regular expression before it times out.


如何使用此功能?另外,使用时需要注意什么?
注意:我是 asking and answering这个问题自 it's encouraged .

最佳答案

我最近研究了这个话题,因为它让我感兴趣,并将在这里介绍要点。相关的 MSDN 文档位于 here,您可以查看 Regex 类以查看新的重载构造函数和静态方法。代码示例可以使用 Visual Studio 11 Developer Preview 运行。
Regex 类接受 TimeSpan 来指定超时持续时间。您可以在应用程序的宏观和微观层面指定超时,它们可以一起使用:

  • 使用 "REGEX_DEFAULT_MATCH_TIMEOUT" method(宏应用程序范围)设置 AppDomain.SetData 属性
  • 传递 matchTimeout 参数(微本地化范围)

  • 当设置 AppDomain 属性时,所有 Regex 操作将使用该值作为默认超时。要覆盖应用程序范围的默认值,您只需将 matchTimeout 值传递给正则表达式构造函数或静态方法。如果未设置 AppDomain 默认值,并且未指定 matchTimeout,则模式匹配不会超时(即原始 pre-.NET 4.5 行为)。

    有两个主要的异常(exception)需要处理:
  • RegexMatchTimeoutException :发生超时时抛出。
  • ArgumentOutOfRangeException:当“matchTimeout 为负数或大于大约 24 天”时抛出。此外,TimeSpan 值为零将导致抛出此错误。

  • 尽管不允许使用负值,但有一个异常(exception):接受 -1 ms 的值。在内部, Regex 类接受 -1 ms,这是 Regex.InfiniteMatchTimeout field 的值,以指示匹配不应超时(即原始 pre-.NET 4.5 行为)。

    使用 matchTimeout 参数

    在下面的示例中,我将演示有效和无效的超时情况以及如何处理它们:
    string input = "The quick brown fox jumps over the lazy dog.";
    string pattern = @"([a-z ]+)*!";
    var timeouts = new[]
    {
    TimeSpan.FromSeconds(4), // valid
    TimeSpan.FromSeconds(-10) // invalid
    };

    foreach (var matchTimeout in timeouts)
    {
    Console.WriteLine("Input: " + matchTimeout);
    try
    {
    bool result = Regex.IsMatch(input, pattern,
    RegexOptions.None, matchTimeout);
    }
    catch (RegexMatchTimeoutException ex)
    {
    Console.WriteLine("Match timed out!");
    Console.WriteLine("- Timeout interval specified: " + ex.MatchTimeout);
    Console.WriteLine("- Pattern: " + ex.Pattern);
    Console.WriteLine("- Input: " + ex.Input);
    }
    catch (ArgumentOutOfRangeException ex)
    {
    Console.WriteLine(ex.Message);
    }
    Console.WriteLine();
    }

    当使用 Regex 类的实例时,您可以访问 MatchTimeout property :
    string input = "The English alphabet has 26 letters";
    string pattern = @"\d+";
    var matchTimeout = TimeSpan.FromMilliseconds(10);
    var sw = Stopwatch.StartNew();
    try
    {
    var re = new Regex(pattern, RegexOptions.None, matchTimeout);
    bool result = re.IsMatch(input);
    sw.Stop();

    Console.WriteLine("Completed match in: " + sw.Elapsed);
    Console.WriteLine("MatchTimeout specified: " + re.MatchTimeout);
    Console.WriteLine("Matched with {0} to spare!",
    re.MatchTimeout.Subtract(sw.Elapsed));
    }
    catch (RegexMatchTimeoutException ex)
    {
    sw.Stop();
    Console.WriteLine(ex.Message);
    }

    使用 AppDomain 属性
    "REGEX_DEFAULT_MATCH_TIMEOUT" 属性用于设置应用程序范围的默认值:
    AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT",
    TimeSpan.FromSeconds(2));

    如果此属性设置为无效的 TimeSpan 值或无效对象,则在尝试使用正则表达式时将抛出 TypeInitializationException

    具有有效属性值的示例:
    // AppDomain default set somewhere in your application
    AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT",
    TimeSpan.FromSeconds(2));

    // regex use elsewhere...
    string input = "The quick brown fox jumps over the lazy dog.";
    string pattern = @"([a-z ]+)*!";

    var sw = Stopwatch.StartNew();
    try
    {
    // no timeout specified, defaults to AppDomain setting
    bool result = Regex.IsMatch(input, pattern);
    sw.Stop();
    }
    catch (RegexMatchTimeoutException ex)
    {
    sw.Stop();
    Console.WriteLine("Match timed out!");
    Console.WriteLine("Applied Default: " + ex.MatchTimeout);
    }
    catch (ArgumentOutOfRangeException ex)
    {
    sw.Stop();
    }
    catch (TypeInitializationException ex)
    {
    sw.Stop();
    Console.WriteLine("TypeInitializationException: " + ex.Message);
    Console.WriteLine("InnerException: {0} - {1}",
    ex.InnerException.GetType().Name, ex.InnerException.Message);
    }
    Console.WriteLine("AppDomain Default: {0}",
    AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT"));
    Console.WriteLine("Stopwatch: " + sw.Elapsed);

    将上面的示例与无效(负)值一起使用会导致抛出异常。处理它的代码将以下消息写入控制台:

    TypeInitializationException: The type initializer for 'System.Text.RegularExpressions.Regex' threw an exception.

    InnerException: ArgumentOutOfRangeException - Specified argument was out of the range of valid values. Parameter name: AppDomain data 'REGEX_DEFAULT_MATCH_TIMEOUT' contains an invalid value or object for specifying a default matching timeout for System.Text.RegularExpressions.Regex.



    在这两个示例中,都没有抛出 ArgumentOutOfRangeException。为了完整起见,代码显示了使用新的 .NET 4.5 Regex 超时功能时可以处理的所有异常。

    覆盖 AppDomain 默认值

    通过指定 AppDomain 值来覆盖 matchTimeout 默认值。在下一个示例中,匹配在 2 秒内超时,而不是默认的 5 秒。
    AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT",
    TimeSpan.FromSeconds(5));

    string input = "The quick brown fox jumps over the lazy dog.";
    string pattern = @"([a-z ]+)*!";

    var sw = Stopwatch.StartNew();
    try
    {
    var matchTimeout = TimeSpan.FromSeconds(2);
    bool result = Regex.IsMatch(input, pattern,
    RegexOptions.None, matchTimeout);
    sw.Stop();
    }
    catch (RegexMatchTimeoutException ex)
    {
    sw.Stop();
    Console.WriteLine("Match timed out!");
    Console.WriteLine("Applied Default: " + ex.MatchTimeout);
    }

    Console.WriteLine("AppDomain Default: {0}",
    AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT"));
    Console.WriteLine("Stopwatch: " + sw.Elapsed);

    结束语

    MSDN 建议在所有正则表达式模式匹配操作中设置一个超时值。但是,它们不会将您的注意力吸引到这样做时需要注意的问题上。我不建议设置 AppDomain 默认值并将其称为一天。你需要知道你的输入和你的模式。如果输入很大,或者模式很复杂,则应使用适当的超时值。这可能还需要衡量您严格执行的正则表达式用法以分配合理的默认值。如果该值不够长,则将超时值任意分配给曾经正常工作的正则表达式可能会导致其中断。如果您认为它可能会过早中止匹配尝试,请在分配值之前测量现有使用情况。

    此外,此功能在处理用户提供的模式时很有用。然而,学习如何编写表现良好的正确模式很重要。对其进行超时以弥补在正确模式构造方面缺乏知识并不是好的做法。

    关于.net - 如何使正则表达式操作超时以防止在 .NET 4.5 中挂起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7616435/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com