Вот отрывок из моего кода:

string[] myStr =
        { 
           " Line1:  active 56:09 -  tst0063, tst0063",
           "Contacts accosiated with line 1 -   tst0063, tst0063",
           "Line 1: 00:00:32      Wrap: 00:00:20 - tst0063, tst0063",
           "Line 1: 00:00:17      Active: 00:00:15 - tst0064, tst0064"
        };

string sPattern = @"^Line(\s*\S*)*tst0063$";
RegexOptions options = RegexOptions.IgnoreCase;

foreach (string s in myStr)
{
    System.Console.Write(s);

    if (System.Text.RegularExpressions.Regex.IsMatch(s, sPattern, options))
    {
        System.Console.WriteLine(" - valid");
    }
    else
    {
        System.Console.WriteLine(" - invalid");
    }
    System.Console.ReadLine();
}

RegularExpressions.Regex.IsMatch зависает при работе с последней строкой. Провел несколько экспериментов, но до сих пор не могу понять, почему зависает, когда в конце строки нет совпадения. Пожалуйста помоги!

3
Lola 26 Дек 2013 в 06:48

2 ответа

Лучший ответ

Вопрос не в том, почему зависает четвертый тест, а в том, почему не работают первые три. Первая строка начинается с пробела, а вторая начинается с Contacts, ни одно из которых не соответствует регулярному выражению ^Line, поэтому первые две попытки сопоставления немедленно завершаются неудачей. Третья строка соответствует регулярному выражению; хотя это занимает намного больше времени, чем должно (по причинам, которые я собираюсь объяснить), это все равно кажется мгновенным.

Четвертое совпадение не удается, потому что строка не соответствует конечной части регулярного выражения: tst0063$. Когда это не удается, механизм регулярных выражений выполняет резервное копирование до переменной части регулярного выражения, (\s*\S*)*, и начинает пробовать все различные способы уместить это в строку. В отличие от третьей строки, на этот раз он должен попробовать каждую возможную комбинацию из нуля или более пробельных символов (\s*), за которыми следует ноль или более непробельных символов (\S*), ноль или более раз, прежде чем он сможет сдаться. Возможности не бесконечны, но вполне могут быть.

Вы, вероятно, думали о [\s\S]*, хорошо известной идиоме для сопоставления любого символа включая символы новой строки . Он используется в JavaScript, который не может заставить точку (.) соответствовать символам разделителя строк. Большинство других вариантов позволяют указать режим сопоставления, который изменяет поведение точки; некоторые называют это режимом DOTALL , но .NET использует более распространенный режим Singleline .

string sPattern = @"^Line.*tst0063$";
RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

Вы также можете использовать модификаторы inline :

string sPattern = @"(?is)^Line.*tst0063$";

ОБНОВЛЕНИЕ: В ответ на ваш комментарий, да, кажется странным, что механизм регулярных выражений не может сказать, что любое совпадение должно заканчиваться на tst0063. Но это не всегда так просто сказать. Сколько усилий нужно приложить для поиска таких ярлыков? И сколько ярлыков вы можете использовать в обычном алгоритме сопоставления, прежде чем все совпадения (как успешные, так и неудачные) станут слишком медленными?

В .NET есть одна из лучших реализаций регулярных выражений: быстрая, мощная и с некоторыми действительно потрясающими функциями. Но вы должны думать о том, что вы говорите ему делать. Например, если вы знаете, что должно быть хотя бы одно из чего-то, используйте +, а не *. Если бы вы следовали этому правилу, у вас не было бы этой проблемы. Это регулярное выражение:

@"^Line(\s+\S+)*tst0063$"

... отлично работает. (\s+\S+)* - это вполне разумный способ сопоставить ноль или несколько слов, где слова определены как один или несколько непробельных символов, отделенных от других слов одним или несколькими пробельными символами. (Это то, что вы пытались сделать?)

3
Alan Moore 26 Дек 2013 в 17:45

Переместите System.Console.ReadLine(); за пределы цикла foreach.

Вы блокируете поток в конце первой итерации цикла, ожидая ввода данных пользователем.

2
Grant Winney 26 Дек 2013 в 02:56