Як або навіщо використовувати ". *?` Краще, ніж ". *"?


9

Я відповів на це запитання на SuperUser, що було щось пов’язане з видом регулярних виразів, що використовуються під час отримання виводу.

Я відповів:

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

А потім, У трьох коментарях до моєї відповіді @Bob написав це:

.*жадібний і може захопити більше, ніж ви хочете. .*?зазвичай краще.

Тоді це,

?є модифікатором на *, що робить його ледачим замість жодного по замовчуванням. Припускаючи, що PCRE.

Я погуглив PCRE, але не міг зрозуміти, у чому сенс цього у моїй відповіді?

і нарешті це,

Я також повинен зазначити, що це регулярний вираз (греп, який виконує POSIX-регекс за замовчуванням), а не глобус оболонки.

Я знаю лише, що таке Regex і дуже базове використання його в команді grep. Отже, я не зміг отримати жоден із цих 3 коментарів, і маю на увазі ці питання:

  • Які відмінності у використанні .*?проти .*?
  • Що краще і за яких обставин? Наведіть приклади.

Також було б корисно розібратися в коментарях, якщо хто міг


ОНОВЛЕННЯ: Як відповідь на питання Чим Regex відрізняється від Shell Globs? @Kusalananda надав це посилання у своєму коментарі.

ПРИМІТКА. Якщо потрібно, будь ласка, прочитайте мою відповідь на це питання, перш ніж відповісти, щоб посилатися на контекст.


Це два дуже різні питання. На перше питання відповідає unix.stackexchange.com/questions/57957/…, тоді як на друге питання залежить від застосування шаблону (не можна сказати, що він "кращий" за будь-яких обставин).
Кусалаланда

Ви можете відредагувати це запитання лише стосовно випуску .*проти .*?. Питання "Різниця між регулярними виразами та глобусами оболонки" вже розглянуто на цьому веб-сайті.
Кусалаланда

Відповіді:


7

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

grep (припускаючи версію GNU) підтримує 4 способи узгодження рядків:

  • Виправлені рядки
  • Основні регулярні вирази (BRE)
  • Розширені регулярні вирази (ERE)
  • Регулярні регулярні вирази (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

Чи можете ви поясніть трохи про .*vs .*??

  • .*використовується для узгодження "найдовшого" 1 можливого шаблону.

  • .*?використовується для узгодження "найкоротшого" 1 можливого шаблону.

На мій досвід, найпотрібніша поведінка, як правило, друга.

Наприклад, скажімо, що у нас є наступний рядок, і ми хочемо лише відповідати теги html 2 , а не вміст між ними:

<title>My webpage title</title>

Тепер порівняйте .*vs .*?:

# 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 з регулярним виразом . Це лише приклад для навчальних цілей, не використовуйте його у виробництві.


Чи можете ви поясніть трохи про .*vs .*??
C0deDaedalus

@ C0deDaedalus Оновлено.
nxnev

9

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

can cats eat plants?

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

Тоді як використання ледачих c.*?sбуде відповідати лише до тих пір, поки не sбуде знайдено перше виникнення , тобто рядок can cats.

З наведеного вище прикладу ви можете зібрати таке:

"Жадібний" означає узгодження найдовшої можливої ​​струни. "Ледачий" означає збіг найкоротшого можливого рядка. Додавання ?до квантору , як *, +, ?або {n,m}роблять його ледачим.


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прикладі використовуються основні глобуси. У [[прикладі баш використовуються розширені кулі. Перша відповідність файлу може бути базовою або розширена на деякій оболонці, як-от налаштування extglobв bash. Обидва в цьому випадку однакові. Греп не міг користуватися глобусами.

    Зірочка в глобусі означає щось інше, ніж зірочка в регулярному виразі :

    * 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 Світ!

    В (базових) снарядах або аварії немає 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: Hello
    grep -oE 'H.*l' <<<"$var" # print: Привіт Worl

  5. Регулярні регулярні вирази, сумісні з Perl:

    grep -oP 'H.*?l # print: Хел

Тільки в PCRE a *?має певне значення синтаксису.
Це робить зірочку лінивою (нечестивою): Лінь замість жадібності .

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

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

Це лише верхівка айсберга, є жадібні, ледачі та послушні чи нав'язливі . Є також lookahead та lookbehind, але вони не стосуються зірочки *.

Існує альтернатива, щоб отримати той же ефект, що і не жадібний регулярний вираз:

$ 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? змінити кількісний показник *. .*жадібний .*?- ні.

Запитання

  • Які відмінності у використанні. ? vs. ?

    • A .*?діє лише в синтаксисі PCRE.
    • A .*більш портативний.
    • Такий же ефект, що і в ненаситній відповідності, можна зробити, замінивши крапку на заперечений діапазон символів: [^a]*
  • Що краще і за яких обставин? Наведіть приклади.
    Краще? Це залежить від мети. Немає кращого, кожен корисний для різних цілей. Я наводив кілька прикладів вище. Вам потрібно більше?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.