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

前端之家收集整理的这篇文章主要介绍了如何超时正则表达式操作以防止挂起.NET 4.5?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
有时可以限制正则表达式操作的模式匹配持续时间可能是有用的.特别地,当使用用户提供的模式来匹配数据时,由于嵌套的量词和过多的后跟踪,模式可能表现不佳(见 catastrophic backtracking).应用超时的一种方法是异步运行正则表达式,但这可能很繁琐,并且会将代码杂乱.

根据what’s new in the .NET Framework 4.5 Developer Preview,它似乎有一个新的内置方法支持这一点:

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

如何使用此功能?此外,使用它时需要注意什么?

注意:自it’s encouraged起,我是asking and answering这个问题.

我最近研究了这个话题,因为它对我有兴趣,并将介绍这里的要点.相关的MSDN文档可用 here,您可以查看Regex类来查看新的重载构造函数和静态方法.代码示例可以使用 Visual Studio 11 Developer Preview运行.

正则表达式类接受TimeSpan指定超时持续时间.您可以在应用程序的宏和微型级别指定超时,并可以一起使用:

>使用AppDomain.SetData method(宏应用范围)设置“REGEX_DEFAULT_MATCH_TIMEOUT”属性
>传递matchTimeout参数(微本地化范围)

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

有两个主要例外处理:

> RegexMatchTimeoutException:发生超时时抛出.
> ArgumentOutOfRangeException:当“matchTimeout为负值或大于约24天”时抛出.另外,TimeSpan值为零将导致抛出.

尽管不允许负值,但有一个例外:接受-1 ms的值.内部的正则表达式类接受-1毫秒,这是Regex.InfiniteMatchTimeout field的值,表示一个匹配不应该超时(即原始的前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();
}

当使用正则表达式类的实例时,您可以访问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,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,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默认值,并一天调用它.您需要知道您的输入并了解您的模式.如果输入较大或模式复杂,则应使用适当的超时值.这可能还需要衡量您的严格执行的正则表达式用法,以指定一致的默认值.任意将正常值分配给用于正常工作的正则表达式可能会导致该值不足够长时间中断.在分配值之前衡量现有用法,如果您认为它可能太早中止匹配尝试.

此外,此功能在处理用户提供的模式时非常有用.然而,学习如何编写能够表现良好的正确模式很重要.拍摄超时以弥补正确模式构建中的缺乏知识是不好的做法.

猜你在找的正则表达式相关文章