Ответ 1
Недавно я изучил эту тему, так как меня это заинтересовало и рассмотрит здесь основные моменты. Соответствующая документация MSDN доступна здесь, и вы можете проверить класс Regex
, чтобы увидеть новые перегруженные конструкторы и статические методы. Сэмплы кода можно запустить с помощью Visual Studio 11 Developer Preview.
Класс Regex
принимает TimeSpan
, чтобы указать продолжительность таймаута. Вы можете указать тайм-аут на макро- и микроуровне вашего приложения, и их можно использовать вместе:
- Задайте свойство
"REGEX_DEFAULT_MATCH_TIMEOUT"
с помощью методаAppDomain.SetData
(область макрообъектива) - Передайте параметр
matchTimeout
(микролокализованная область)
Когда свойство AppDomain
установлено, все операции Regex
будут использовать это значение в качестве таймаута по умолчанию. Чтобы переопределить общепринятое значение по умолчанию, вы просто передаете значение matchTimeout
конструктору regex или статическому методу. Если параметр AppDomain
по умолчанию не установлен, а matchTimeout
не указан, то совпадение шаблонов не будет превышать тайм-аут (т.е. Исходное поведение pre -.NET 4.5).
Существует два основных исключения:
-
RegexMatchTimeoutException
: вызывается при возникновении тайм-аута. -
ArgumentOutOfRangeException
: выбрано, когда "matchTimeout
отрицательно или больше, чем приблизительно 24 дня". Кроме того, значениеTimeSpan
, равное нулю, приведет к его выбросу.
Несмотря на то, что отрицательные значения не разрешены, существует одно исключение: принимается значение -1 мс. Внутренний класс Regex
принимает -1 мс, что является значением поля Regex.InfiniteMatchTimeout
, чтобы указать, что совпадение не должно быть таймаутом ( т.е. исходное поведение 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: инициализатор типа для 'System.Text.RegularExpressions.Regex' выбрал исключение.
InnerException: ArgumentOutOfRangeException - указанный аргумент был из диапазона допустимых значений. Имя параметра: данные AppDomain 'REGEX_DEFAULT_MATCH_TIMEOUT' содержит недопустимое значение или объект для указание времени ожидания по умолчанию для 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 и называть его днем. Вы должны знать свой вклад и знать свои шаблоны. Если вход большой, или шаблон сложный, следует использовать соответствующее значение тайм-аута. Это может также привести к тому, что ваши критически используемые регулярные выражения будут назначать нормальные значения по умолчанию. Произвольное присвоение значения тайм-аута регулярному выражению, которое используется для работы нормально, может привести к его разрыву, если значение недостаточно. Измерьте существующие способы использования, прежде чем назначать значение, если вы считаете, что оно может прервать попытку сопоставления слишком рано.
Кроме того, эта функция полезна при обращении с шаблонами, поставляемыми пользователем. Тем не менее, важно научиться писать правильные шаблоны, которые хорошо работают. Повреждение тайм-аута на нем, чтобы компенсировать недостаток знаний в правильном построении шаблонов, не является хорошей практикой.