Подтвердить что ты не робот

Расширение синтаксиса регулярных выражений, чтобы сказать: "не содержит текст XYZ"

У меня есть приложение, в котором пользователи могут указывать регулярные выражения в нескольких местах. Они используются при запуске приложения для проверки соответствия текста (например, URL-адресов и HTML) регулярным выражениям. Часто пользователи хотят иметь возможность сказать, где текст соответствует ABC и не соответствует XYZ. Чтобы облегчить им это сделать, я думаю о распространении синтаксиса регулярных выражений в моем приложении с возможностью сказать "и не содержит шаблон". Любые предложения по хорошему способу сделать это?

Мое приложение написано на С#.NET 3.5.

Мой план (до того, как я получил удивительные ответы на этот вопрос...)

В настоящее время я думаю об использовании символа ¬: что-либо до того, как символ ¬ является нормальным регулярным выражением, что-либо после символа ¬ является регулярным выражением, которое не может соответствовать в тестируемом тексте.

Поэтому я мог бы использовать некоторые регулярные выражения, подобные этому (надуманному) примеру:

on (this|that|these) day(s)?¬(every|all) day(s) ?

Что, например, будет соответствовать "в этот день мужчина сказал...", но не будет соответствовать "в этот день и каждый день после того, как будет...".

В моем коде, обрабатывающем регулярное выражение, я просто разделил две части регулярного выражения и обрабатывал их отдельно, например:

    public bool IsMatchExtended(string textToTest, string extendedRegex)
    {
        int notPosition = extendedRegex.IndexOf('¬');

        // Just a normal regex:
        if (notPosition==-1)
            return Regex.IsMatch(textToTest, extendedRegex);

        // Use a positive (normal) regex and a negative one
        string positiveRegex = extendedRegex.Substring(0, notPosition);
        string negativeRegex = extendedRegex.Substring(notPosition + 1, extendedRegex.Length - notPosition - 1);

        return Regex.IsMatch(textToTest, positiveRegex) && !Regex.IsMatch(textToTest, negativeRegex);
    }

Любые предложения по лучшему способу реализации такого расширения? Мне нужно быть немного умнее о разделении строки на символ ¬, чтобы она могла быть экранирована, поэтому не просто использовать простое разделение подстроки() выше. Что еще нужно учитывать?

Альтернативный план

При написании этого вопроса я также встретил этот ответ, который предлагает использовать что-то вроде этого:

^(?=(?:(?!negative pattern).)*$).*?positive pattern

Поэтому я мог бы просто посоветовать людям использовать шаблон, например, вместо моего первоначального плана, когда они хотят НЕ соответствовать определенному тексту.

Будет ли это эквивалентно моему первоначальному плану? Я думаю, что это довольно дорогостоящий способ сделать это мудрый, и поскольку я иногда разбираю большие html-документы, это может быть проблемой, тогда как я полагаю, что мой первоначальный план был бы более результативным. Любые мысли (помимо очевидного: "попробуйте оба и измерьте их!" )?

Возможно, уместно для производительности: иногда будет несколько слов или более сложное регулярное выражение, которое не может быть в тексте, например (каждый | все) в моем примере выше, но с несколькими вариантами.

Почему!?

Я знаю, что мой оригинальный подход кажется странным, например. почему не просто два регулярных выражения!? Но в моем конкретном приложении администраторы предоставляют регулярные выражения, и было бы довольно сложно дать им возможность предоставлять два регулярных выражения везде, где они могут его предоставить. Намного проще в этом случае иметь синтаксис для NOT - просто доверьтесь мне в этой точке.

У меня есть приложение, которое позволяет администраторам определять регулярные выражения в разных точках конфигурации. Регулярные выражения используются только для проверки соответствия текста или URL определенного шаблона; замены не выполняются, и группы захвата не используются. Однако часто они хотели бы указать шаблон, в котором говорится: "Где ABC отсутствует в тексте". Как правило, трудно выполнить НЕ соответствие в регулярных выражениях, поэтому обычный способ состоит в том, чтобы иметь два регулярных выражения: один для указания шаблона, который должен быть сопоставлен, и один для указания шаблона, который не должен быть сопоставлен. Если первый соответствует, а второй - нет, текст соответствует. В моем приложении было бы очень много работы, чтобы добавить возможность иметь второе регулярное выражение в каждом месте, которое пользователи могут предоставить сейчас, поэтому я хотел бы расширить синтаксис регулярных выражений, чтобы сказать " и не содержать шаблон.

4b9b3361

Ответ 1

Вам не нужно вводить новый символ. Уже существует поддержка того, что вам нужно в большинстве движков регулярных выражений. Это просто вопрос изучения и применения.

У вас есть проблемы с производительностью, но вы ее протестировали? Вы оценили и продемонстрировали эти проблемы с производительностью? Это, вероятно, будет просто отлично.

Regex работает для многих людей во многих разных сценариях. Вероятно, это соответствует вашим требованиям.

Кроме того, сложное регулярное выражение, которое вы нашли на другом вопросе SO, может быть упрощено. Есть простые выражения для негативных и позитивных взглядов и взглядов.
?! ?<! ?= ?<=


Некоторые примеры

Предположим, что образец текста <tr valign='top'><td>Albatross</td></tr>

Учитывая следующие выражения регулярных выражений, вы увидите следующие результаты:

  • tr - соответствие
  • td - соответствие
  • ^td - нет соответствия
  • ^tr - нет соответствия
  • ^<tr - соответствие
  • ^<tr>.*</tr> - нет соответствия
  • ^<tr.*>.*</tr> - соответствие
  • ^<tr.*>.*</tr>(?<tr>) - соответствие
  • ^<tr.*>.*</tr>(?<!tr>) - нет соответствия
  • ^<tr.*>.*</tr>(?<!Albatross) - соответствие
  • ^<tr.*>.*</tr>(?<!.*Albatross.*) - нет соответствия
  • ^(?!.*Albatross.*)<tr.*>.*</tr> - нет соответствия

Пояснения

Первые два соответствуют, потому что регулярное выражение может применяться в любом месте в образце (или тестовой) строке. Вторые два не совпадают, потому что ^ говорит "начинать в начале", а тестовая строка не начинается с td или tr - начинается с левой угловой скобки.

Пятый пример соответствует, потому что тестовая строка начинается с <tr. Шестой нет, потому что он хочет, чтобы строка образца начиналась с <tr> с скобкой угла закрытия сразу после tr, но в фактической тестовой строке открытие tr включает атрибут valign, поэтому то, что следует tr, является пространством. 7-е регулярное выражение показывает, как разрешить пространство и атрибут с помощью подстановочных знаков.

8-е регулярное выражение применяет положительное утверждение lookbehind к концу регулярного выражения, используя ?<. Он говорит, соответствует всему регулярному выражению, только если то, что сразу предшествует курсору в тестовой строке, соответствует тому, что в parens, следуя ?<. В этом случае следует, что tr>. После оценки `` ^. * , the cursor in the test string is positioned at the end of the test string. Therefore, the tr > `сопоставляется с концом тестовой строки, которая оценивается как TRUE. Поэтому положительный lookbehind оценивается как true, поэтому общее регулярное выражение соответствует.

В девятом примере показано, как вставить отрицательное утверждение lookbehind, используя ?<!. В основном это говорит: "Разрешить регулярному выражению соответствовать тому, что прямо за курсором в этой точке не соответствует тому, что следует за ?<! в parens, которое в этом случае равно tr>. Бит регулярного выражения, предшествующий утверждению, ^<tr.*>.*</tr> совпадает с концом строки и включает конец строки. Поскольку шаблон tr> соответствует концу строки, но это отрицательное утверждение, поэтому оно вычисляется как FALSE, что означает, что 9-й пример НЕ является совпадением.

В десятом примере используется другое отрицательное утверждение lookbehind. В основном это говорит о том, что "разрешить регулярное выражение соответствовать тому, что прямо за курсором в этой точке не соответствует тому, что в parens, в данном случае Albatross. Бит регулярного выражения, предшествующий утверждению ^<tr.*>.*</tr>, соответствует вплоть до конец строки. Проверка" Albatross" в конце строки дает отрицательное совпадение, потому что тестовая строка заканчивается на </tr>. Поскольку шаблон внутри парнеров отрицательного lookbehind НЕ соответствует, это означает отрицательный lookbehind оценивает значение TRUE, что означает, что 10-й пример является совпадением.

11-й пример расширяет отрицательный lookbehind для включения подстановочных знаков; на английском языке результат отрицательного lookbehind "соответствует только если предыдущая строка не включает слово Albatross". В этом случае тестовая строка DOES включает слово, отрицательный lookbehind оценивается как FALSE, а 11-е регулярное выражение не соответствует.

В 12-м примере используется отрицательное утверждение. Подобно lookbehinds, lookaheads имеют нулевую ширину - они не перемещают курсор внутри тестовой строки для целей сопоставления строк. Смотри в этом случае, сразу же отклоняет строку, потому что .*Albatross.* соответствует; потому что это отрицательный результат, он вычисляет FALSE, что означает, что общее регулярное выражение не соответствует, что означает, что проверка регулярного выражения на тестовой строке прекращается.

пример 12 всегда оценивает то же логическое значение, что и в примере 11, но во время выполнения он ведет себя по-разному. В ex 12 отрицательная проверка выполняется сначала, при остановках немедленно. В ex 11 полное regex применяется и оценивается до TRUE, прежде чем проверяется утверждение lookbehind. Таким образом, вы можете видеть, что при сравнении lookaheads и lookbehind могут возникать различия в производительности. Какой из них подходит вам, зависит от того, на что вы согласны, и относительной сложностью шаблона "положительного соответствия" и шаблона "отрицательного соответствия".

Подробнее об этом читайте в http://www.regular-expressions.info/

Или получить инструмент оценки регулярных выражений и попробовать некоторые тесты.

как этот инструмент:
enter image description here

источник и двоичный файл

Ответ 2

Вы можете легко достичь своих целей, используя одно регулярное выражение. Вот пример, который демонстрирует один из способов сделать это. Это регулярное выражение соответствует строке, содержащей "cat" AND "lion" AND "tiger", но НЕ содержит "dog" ИЛИ "wolf" ИЛИ "hyena":

if (Regex.IsMatch(text, @"
    # Match string containing all of one set of words but none of another.
    ^                # anchor to start of string.
    # Positive look ahead assertions for required substrings.
    (?=.*?  cat   )  # Assert string has: 'cat'.
    (?=.*?  lion  )  # Assert string has: 'lion'.
    (?=.*?  tiger )  # Assert string has: 'tiger'.
    # Negative look ahead assertions for not-allowed substrings.
    (?!.*?  dog   )  # Assert string does not have: 'dog'.
    (?!.*?  wolf  )  # Assert string does not have: 'wolf'.
    (?!.*?  hyena )  # Assert string does not have: 'hyena'.
    ",
    RegexOptions.Singleline | RegexOptions.IgnoreCase |
    RegexOptions.IgnorePatternWhitespace)) {
    // Successful match
} else {
    // Match attempt failed
} 

Вы можете увидеть нужный шаблон. При сборке регулярного выражения обязательно выполняйте каждую из предоставленных пользователем подстрок с помощью метода Regex.escape(), чтобы избежать любых метасимволов, которые могут содержать (т.е. (, ), | и т.д.). Кроме того, вышеупомянутое регулярное выражение записывается в режиме свободного пробела для удобства чтения. Ваше регулярное выражение не должно использовать этот режим, иначе пробелы внутри пользовательских подстрок будут проигнорированы.

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

Отметим также, что отрицательное утверждение можно сделать более эффективным, используя следующий альтернативный синтаксис:

(?!.*?(?:dog|wolf|hyena))