Как или почему использование `. *?` Лучше, чем `. *`?

9

Я ответил на этот вопрос в SuperUser, который имел отношение к виду регулярных выражений, используемых при подборе вывода.

Ответ, который я дал, был таков:

 tail -f log | grep "some_string.*some_string"

И затем, в трех комментариях к моему ответу @Bob написал это:

.*жадный и может захватить больше, чем вы хотите. .*?обычно лучше.

Тогда это,

?является модификатором на *, что делает его ленивым вместо жадного по умолчанию. Предполагая PCRE.

Я гуглил PCRE, но не мог понять, каково значение этого в моем ответе?

и наконец это,

Я также должен отметить, что это регулярное выражение (по умолчанию grep делает регулярное выражение POSIX), а не глобус оболочки.

Я знаю только, что такое Regex и как его использовать в команде grep. Итак, я не смог получить ни одного из этих 3 комментариев, и у меня есть следующие вопросы:

  • Каковы различия в использовании .*?против .*?
  • Что лучше и при каких обстоятельствах? Пожалуйста, приведите примеры.

Также было бы полезно понять комментарии, если кто-то мог


ОБНОВЛЕНИЕ: Как ответ на вопрос Чем Regex отличается от Shell Globs? @Kusalananda предоставил эту ссылку в своем комментарии.

ПРИМЕЧАНИЕ: При необходимости, пожалуйста, прочитайте мой ответ на этот вопрос, прежде чем отвечать для ссылки на контекст.

C0deDaedalus
источник
Это два совершенно разных вопроса. На первый вопрос отвечает unix.stackexchange.com/questions/57957/…, а второй вопрос зависит от применения шаблона (нельзя сказать, что он «лучше» при любых обстоятельствах).
Кусалананда
Вы можете редактировать этот вопрос только о .*сравнении .*?вопроса. Вопрос «Разница между регулярными выражениями и глобусами оболочки» уже обсуждался на этом сайте.
Кусалананда

Ответы:

7

Ашок уже указал на разницу между .*и .*?, поэтому я просто предоставлю некоторую дополнительную информацию.

grep (предполагается версия GNU) поддерживает 4 способа сопоставления строк:

  • Фиксированные строки
  • Основные регулярные выражения (BRE)
  • Расширенные регулярные выражения (ERE)
  • Perl-совместимые регулярные выражения (PCRE)

grep по умолчанию использует BRE.

BRE и ERE описаны в главе о регулярных выражениях POSIX, а PCRE - на официальном веб-сайте . Обратите внимание, что функции и синтаксис могут различаться в разных реализациях.

Стоит сказать, что ни BRE, ни ERE не поддерживают лень :

Поведение нескольких смежных символов дублирования ('+', '*', '?' И интервалов) дает неопределенные результаты.

Так что если вы хотите использовать эту функцию, вам нужно будет использовать вместо этого PCRE:

# BRE greedy
$ grep -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# BRE lazy
$ grep -o 'c.*\?s' <<< 'can cats eat plants?'
can cats eat plants

# ERE greedy
$ grep -E -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# ERE lazy
$ grep -E -o 'c.*?s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE greedy
$ grep -P -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE lazy
$ grep -P -o 'c.*?s' <<< 'can cats eat plants?'
can cats

Редактировать 1

Не могли бы вы объяснить немного о .*против .*??

  • .*используется для соответствия «самой длинной» 1 модели.

  • .*?используется для соответствия «кратчайшему» 1 шаблону.

По моему опыту, наиболее желаемое поведение - обычно второе.

Например, допустим, у нас есть следующая строка, и мы хотим соответствовать только HTML-тегам 2 , а не содержимому между ними:

<title>My webpage title</title>

Теперь сравните .*против .*?:

# Greedy
$ grep -P -o '<.*>' <<< '<title>My webpage title</title>'
<title>My webpage title</title>

# Lazy
$ grep -P -o '<.*?>' <<< '<title>My webpage title</title>'
<title>
</title>

1. Значение «самый длинный» и «самый короткий» в контексте регулярных выражений, как указал Кусалананда, немного сложнее . Обратитесь к официальной документации для получения дополнительной информации.
2. Не рекомендуется разбирать html с регулярным выражением . Это просто пример для образовательных целей, не используйте его в производстве.

nxnev
источник
Не могли бы вы объяснить немного о .*против .*??
C0deDaedalus
@ C0deDaedalus Обновлено.
nxnev
9

Предположим, я беру строку вроде:

can cats eat plants?

Использование жадного алгоритма c.*sбудет соответствовать всей строке, так как она начинается cи заканчивается s, будучи жадным оператором, она продолжает совпадать до последнего появления s.

Принимая во внимание, что использование ленивых c.*?sбудет соответствовать только до тех пор, пока не sбудет найдено первое вхождение , то есть строка can cats.

Из приведенного выше примера вы можете получить следующее:

«Жадность» означает соответствие самой длинной возможной строки. «Ленивый» означает соответствие самой короткой возможной строки. Добавление ?к квантору , как *, +, ?или {n,m}делают его ленивым.

Ashok
источник
1
«Кратчайший возможный» был бы cats, поэтому он не применяет строго кратчайший в этом смысле.
Кусалананда
2
@Kusalananda верно, не строго в этом смысле, но «самое короткое из возможных» здесь означает между первым появлением как c, так и s.
Ашок
1

Строка может быть сопоставлена ​​несколькими способами (от простого до более сложного):

  1. В качестве статической строки (предположим, var = 'Hello World!'):

    [ "$var" = "Hello World!" ] && echo yes
    echo "$var" | grep -F "Hello"
    grep -F "Hello" <<<"$var"

  2. Как шар:

    echo ./* # список всех файлов в pwd.
    case $var in (*Worl*) echo yes;; (*) echo no;; esac
    [[ "$var" == *"Worl"* ]] && echo yes

    Есть основные и расширенные шары. В caseпримере используются основные глобусы. В [[примере с bash используются расширенные глобусы. Первое совпадение файла может быть базовым или расширенным в некоторой оболочке, например, extglobв bash. Оба идентичны в этом случае. Grep не мог использовать шарики.

    Звездочка в глобе означает нечто иное, чем звездочка в регулярном выражении :

    * matches any number (including none) ofлюбые символы .
    * matches any number (including none) of theпредыдущего элемента .

  3. В качестве основного регулярного выражения (BRE):

    echo "$var" | sed 's/W.*d//' # print: Привет!
    grep -o 'W.*d' <<<"$var" # print World!

    В базовых оболочках или в awk нет BRE.

  4. Расширенные регулярные выражения (ERE):

    [[ "$var" =~ (H.*l) ]] # match: Hello Worl
    echo "$var" | sed -E 's/(d|o)//g' # print: Hell Wrl!
    awk '/W.*d/{print $1}' <<<"$var" # print: Здравствуйте,
    grep -oE 'H.*l' <<<"$var" # print: Здравствуйте, Worl

  5. Perl-совместимые регулярные выражения:

    grep -oP 'H.*?l # print: Hel

Только в PCRE *?есть определенный синтаксический смысл.
Это делает звездочку леиво (ungreedy): Лень Вместо жадности .

$ grep -oP 'e.*l' <<<"$var"
ello Worl

$ grep -oP 'e.*?l' <<<"$var"
el

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

Есть альтернатива, чтобы получить тот же эффект, что и не жадное регулярное выражение:

$ grep -o 'e[^o]*o' <<<"$var"
ello

Идея очень проста: не используйте точку ., отмените следующий символ для соответствия [^o]. С веб-тегом:

$ grep -o '<[^>]*>' <<<'<script type="text/javascript">document.write(5 + 6);</script>'
<script type="text/javascript">
</script>

Выше следует полностью уточнить все комментарии @Bob 3. Перефразируя:

  • A. * - это обычное регулярное выражение, а не глобус.
  • Только регулярное выражение может быть совместимым с PCRE.
  • В PCRE: а? изменить * квантификатор. .*жадный .*?нет.

Вопросов

  • Каковы различия в использовании. ? против ?

    • A .*?действителен только в синтаксисе PCRE.
    • А .*более портативный.
    • Тот же эффект, что и для не жадного совпадения, можно сделать, заменив точку отрицательным диапазоном символов: [^a]*
  • Что лучше и при каких обстоятельствах? Пожалуйста, приведите примеры.
    Лучше? Это зависит от цели. Нет лучше, каждый полезен для разных целей. Я привел несколько примеров выше. Вам нужно больше?

Исаак
источник