Поиск отсутствия слов в регулярном выражении

#regex #expression #words

#регулярное выражение #выражение #слова

Вопрос:

Я видел примеры поиска отсутствия символов в регулярном выражении, я пытаюсь найти отсутствие слов в регулярном выражении (вероятно, используя отрицательный взгляд назад).

У меня есть строки кода, подобные этому:

Пример первый:

 protected static readonly string BACKGROUND_MUSIC_NAME = "Music_Mission_Complete_Loop_audio";
  

И вот еще одно:

 mainWindow.Id = "MainWindow";
  

Последнее:

 mainStoLabel.Text = "#stb_entry_clah";
  

Я хочу захватить только среднее, найдя все строки, подобные этим, которым a.) не предшествует «#» в фактической строке между кавычками, и b.) вообще не предшествует слово «только для чтения».

Мое текущее регулярное выражение таково:

 .*W=W"[^#].*"
  

В нем приведены два первых примера. Теперь я просто хочу сузить верхний пример. Как мне зафиксировать отсутствие целых слов (не символов).

Спасибо.

Ответ №1:

Ошибка в вашем утверждении «Прогноз отрицания» заключается в том, что вы неправильно собрали его в соответствии с общим случаем. Вам нужно, чтобы его утверждение применялось к каждой позиции символа по мере продвижения вперед. Оно применяется только к одной возможной точке так, как вы ее написали, тогда как вам нужно, чтобы оно применялось ко всем из них. Смотрите ниже, как вы должны это сделать, чтобы сделать это правильно.

Вот рабочая демонстрация, которая демонстрирует два разных подхода:

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

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

Демонстрационный язык — Perl, но одни и те же шаблоны и логика должны работать практически везде.

 #!/usr/bin/perl

while (<DATA>) {
    chomp;
#
# First demo: use a complicated regex to get desired part only
#
    my($label) = m{
        ^                           # start at the beginning
        (?:                         # noncapture group:
            (?! b readonly b )    #   no "readonly" here
            .                       #   now advance one character
        )                           # repeated 1 or more times
        s* = s*                   # skip an equals sign w/optional spaces
        " ( [^#"] [^"]* ) "         # capture #1: quote-delimited text
                                    #   BUT whose first char isn't a "#"
    }x;

    if (defined $label) {
        print "Demo One: found label <$label> at line $.n";
    }
#
# Second demo: This time use simpler patterns, several
#
    my($lhs, $rhs) = m{
        ^                       # from the start of line
        ( [^=]  )               # capture #1: 1 or more non-equals chars
        s* = s*               # skip an equals sign w/optional spaces
        " ( [^"]  ) "           # capture #2: all quote-delimited text
    }x;

    unless ($lhs =~ /b readonly b/x || $rhs =~ /^#/) {
        print "Demo Two: found label <$rhs> at line $.n";
    }

}
__END__
protected static readonly string BACKGROUND_MUSIC_NAME = "Music_Mission_Complete_Loop_audio";
mainWindow.Id = "MainWindow";
mainStoLabel.Text = "#stb_entry_clah";
  

У меня есть два совета. Первое — убедиться, что вы ВСЕГДА используете /x mode, чтобы создавать документированные и поддерживаемые регулярные выражения. Во-вторых, гораздо чище выполнять что-то понемногу, как во втором решении, а не все сразу, как в первом.

Комментарии:

1. Вау, это было очень, очень полезно. Я также ценю совет. Спасибо.

Ответ №2:

Я не совсем понимаю ваш вопрос, негативный прогноз будет выглядеть следующим образом:

 (?!.*readonly)(?:.*s=s"[^#].*")
  

Первая часть будет соответствовать, если в строке нет слова «только для чтения».

Какой язык вы используете?

Чему вы хотите соответствовать, только второму примеру, я правильно понял это?

Комментарии:

1. Это совпадает eadonly string BACKGROUND_MUSIC_NAME = "Music_Mission_Complete_Loop_audio" с первым примером?

2. Спасибо за ответ. Проблема заключается в сопоставлении слов, а не символов. Вы правы, в том смысле, что я пытаюсь сопоставить вторую строку только путем поиска слова readonly ранее в строке. Если бы это был просто поиск символа, это было бы очень просто. Я пытаюсь найти решение, оглядываясь назад на целое слово.

Ответ №3:

^[^"=]*(?<!(^|s)readonlys.*)s*=s*"[^#].*" кажется, соответствует вашим потребностям:

  • все, что стоит перед первым знаком равенства, не должно содержать readonly кавычек или
  • readonly распознается не по границам слов, а по пробелам (за исключением начала строки)
  • знак равенства может быть окружен произвольным пробелом
  • за знаком равенства должна следовать строка, заключенная в кавычки
  • строка, заключенная в кавычки, не должна начинаться с #

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

Примечание: согласно вашему собственному регулярному выражению, это отбрасывает все после последней цитаты (не соответствует точке с запятой в ваших примерах)

Комментарии:

1. Для улучшения читаемости и упрощения построения регулярных выражений можно поместить логические ограничения до или после основного шаблона (обычно перед), вот так: ^(?![^"=]*(^|s)readonlys)[^"=]*s*=s*"[^#].*" . Этот подход предлагает альтернативное решение, только с другим видом поиска (в другой позиции), за исключением того, что оба шаблона одинаковы.

Ответ №4:

Вам абсолютно необходимо указать язык. Отрицательный прогноз — это то, что вам нужно.

Посмотрите на этом сайте список того, как это сделать в Delphi, GNU (Linux), Groovy, Java, JavaScript, .NET, PCRE (C / C ), Perl, PHP, POSIX, PowerShell, Python, R, REALbasic, Ruby, Tcl, VBScript, Visual Basic 6, wxWidgets, XML Schema, XQuery amp; XPath