Comment ou pourquoi utiliser `. *?` Est meilleur que `. *`?

9

J'ai répondu à cette question sur SuperUser qui était liée au type d'expressions régulières utilisées lors de la réception d'une sortie.

La réponse que j'ai donnée était la suivante:

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

Et puis, en trois commentaires à ma réponse @Bob a écrit ceci:

.*est gourmand et peut capturer plus que vous ne le souhaitez. .*?est généralement mieux.

Ensuite ceci,

l' ?est un modificateur sur *, ce qui rend paresseux au lieu de la valeur par défaut gourmand. En supposant PCRE.

J'ai cherché sur Google PCRE, mais je n'ai pas pu comprendre quelle est la signification de cela dans ma réponse?

et enfin ça,

Je dois également souligner qu'il s'agit d'expressions régulières (grep exécutant des expressions régulières POSIX par défaut), et non d'un shell global.

Je sais seulement ce qu'est un Regex et son utilisation très basique dans la commande grep. Donc, je n'ai pu obtenir aucun de ces 3 commentaires et j'ai ces questions à l'esprit:

  • Quelles sont les différences d'utilisation de .*?vs .*?
  • Quel est le meilleur et dans quelles circonstances? Veuillez fournir des exemples.

Il serait également utile de comprendre les commentaires, si quelqu'un pouvait


MISE À JOUR: En réponse à la question En quoi Regex diffère-t-il des Shell Globs? @Kusalananda a fourni ce lien dans son commentaire.

REMARQUE: Si nécessaire, veuillez lire ma réponse à cette question avant de répondre pour vous référer au contexte.

C0deDaedalus
la source
Ce sont deux questions très différentes. La première question est répondue par unix.stackexchange.com/questions/57957/… tandis que la deuxième question dépend de l'application du modèle (on ne peut pas dire qu'il soit "meilleur" en toutes circonstances).
Kusalananda
Vous pouvez modifier cette question pour qu'elle ne concerne que le problème .*vs. .*?La question "différence entre expressions régulières et globes shell" a déjà été abordée sur ce site.
Kusalananda

Réponses:

7

Ashok déjà fait remarquer la différence entre .*et .*?, donc je vais juste fournir des informations supplémentaires.

grep (en supposant la version GNU) prend en charge 4 façons de faire correspondre les chaînes:

  • Chaînes fixes
  • Expressions régulières de base (BRE)
  • Expressions régulières étendues (ERE)
  • Expressions régulières compatibles Perl (PCRE)

grep utilise BRE par défaut.

BRE et ERE sont documentés dans le chapitre Expressions régulières de POSIX et PCRE est documenté sur son site officiel . Veuillez noter que les fonctionnalités et la syntaxe peuvent varier entre les implémentations.

Il vaut la peine de dire que ni BRE ni ERE ne supportent la paresse :

Le comportement de plusieurs symboles de duplication adjacents ('+', '*', '?' Et intervalles) produit des résultats indéfinis.

Donc, si vous souhaitez utiliser cette fonctionnalité, vous devrez utiliser PCRE à la place:

# 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

Modifier 1

Pourriez-vous expliquer un peu le .*vs .*??

  • .*est utilisé pour faire correspondre le motif "le plus long" 1 possible.

  • .*?est utilisé pour faire correspondre le motif "le plus court" 1 possible.

D'après mon expérience, le comportement le plus recherché est généralement le deuxième.

Par exemple, disons que nous avons la chaîne suivante et que nous voulons seulement faire correspondre les balises html 2 , pas le contenu entre elles:

<title>My webpage title</title>

Comparez maintenant .*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. La signification de "plus longue" et "plus courte" dans un contexte d'expression régulière est un peu délicate, comme l'a souligné Kusalananda . Reportez-vous à la documentation officielle pour plus d'informations.
2. Il n'est pas recommandé d'analyser le html avec regex . Ceci est juste un exemple à des fins éducatives, ne l'utilisez pas en production.

nxnev
la source
Pourriez-vous expliquer un peu le .*vs .*??
C0deDaedalus
@ C0deDaedalus Mis à jour.
2018
9

Supposons que je prenne une chaîne comme:

can cats eat plants?

Utiliser le gourmand c.*scorrespondra à la chaîne entière car il commence par cet se termine par s, étant un opérateur gourmand, il continue de correspondre jusqu'à l'occurrence finale de s.

Alors que l'utilisation du paresseux c.*?sne correspondra que jusqu'à ce que la première occurrence de ssoit trouvée, c'est-à-dire la chaîne can cats.

À partir de l'exemple ci-dessus, vous pourriez être en mesure de recueillir que:

"Gourmand" signifie correspondre à la chaîne la plus longue possible. "Paresseux" signifie correspondre à la chaîne la plus courte possible. Ajout d' un ?à un quantificateurs comme *, +, ?ou {n,m}rend paresseux.

Ashok
la source
1
"Le plus court possible" serait cats, donc il ne s'agit pas d'appliquer le "plus court possible" strictement dans ce sens.
Kusalananda
2
@Kusalananda true, pas strictement dans ce sens mais "le plus court possible" signifie ici entre la première occurrence de c et de s.
Ashok
1

Une chaîne peut être mise en correspondance de plusieurs manières (du plus simple au plus complexe):

  1. En tant que chaîne statique (supposez var = 'Hello World!'):

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

  2. En tant que glob:

    echo ./* # liste tous les fichiers dans pwd.
    case $var in (*Worl*) echo yes;; (*) echo no;; esac
    [[ "$var" == *"Worl"* ]] && echo yes

    Il existe des globes de base et étendus. L' caseexemple utilise des globes de base. L' [[exemple bash utilise des globs étendus. La première correspondance de fichier peut être basique ou étendue sur un shell comme la configuration extglobde bash. Les deux sont identiques dans ce cas. Grep n'a pas pu utiliser de globes.

    L'astérisque dans un glob signifie quelque chose de différent d'un astérisque dans une expression régulière :

    * matches any number (including none) oftous les caractères .
    * matches any number (including none) of theélément précédent .

  3. En tant qu'expression régulière de base (BRE):

    echo "$var" | sed 's/W.*d//' # print: Bonjour!
    grep -o 'W.*d' <<<"$var" # print Monde!

    Il n'y a pas de BRE dans les coques (de base) ou awk.

  4. Expressions régulières étendues (ERE):

    [[ "$var" =~ (H.*l) ]] # match: Bonjour Worl
    echo "$var" | sed -E 's/(d|o)//g' # print: Hell Wrl!
    awk '/W.*d/{print $1}' <<<"$var" # print: Bonjour
    grep -oE 'H.*l' <<<"$var" # print: Bonjour le monde

  5. Expressions régulières compatibles Perl:

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

Uniquement dans un PCRE a *?a une signification syntaxique spécifique.
Cela rend l'astérisque paresseux (non-cupide): la paresse au lieu de la gourmandise .

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

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

Ce n'est que la pointe de l'iceberg, il y a des gourmands, des paresseux , des dociles ou des possessifs . Il y a aussi lookahead et lookbehind mais ceux-ci ne s'appliquent pas à l'astérisque *.

Il existe une alternative pour obtenir le même effet qu'une expression régulière non gourmande:

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

L'idée est très simple: n'utilisez pas de point ., annulez le caractère suivant à faire correspondre [^o]. Avec une balise Web:

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

Ce qui précède devrait clarifier complètement tous les commentaires @Bob 3. Paraphraser:

  • Un. * Est un regex commun, pas un glob.
  • Seul un regex pourrait être compatible PCRE.
  • Dans PCRE: un? modifier le * quantificateur. .*est gourmand .*?n'est pas.

Des questions

  • Quelles sont les différences d'utilisation de. ? contre. . ?

    • A .*?n'est valide que dans la syntaxe PCRE.
    • A .*est plus portable.
    • Le même effet qu'une correspondance non gourmande pourrait être fait en remplaçant le point par une plage de caractères inversée: [^a]*
  • Quel est le meilleur et dans quelles circonstances? Veuillez fournir des exemples.
    Meilleur? Cela dépend de l'objectif. Il n'y a rien de mieux, chacun est utile à des fins différentes. J'ai fourni plusieurs exemples ci-dessus. En avez-vous besoin de plus?

Isaac
la source