3
votes

Regex récursif pour tout faire correspondre entre parenthèses (PCRE)

Je suis surpris de ne pas trouver facilement une question similaire avec une réponse sur SO. Je voudrais tout faire correspondre dans certaines fonctions. L'idée est de supprimer les fonctions inutiles.

(?<name>\w+)\s*\(\K
(?<e>
     [^()]+
     |
     [^()]*
         \((?&e)\)
     [^()]*
)*
(?=\))

J'essaie donc de faire correspondre tout ce qui est dans l'appel de fonction qui peut inclure des parenthèses. Voici mon regex PCRE:

foo(some (content)) --> some (content)

https: / /regex101.com/r/gfMAIM/1

Malheureusement, cela ne fonctionne pas et je ne comprends pas vraiment pourquoi.


3 commentaires

L'appel inspecté de la fonction est-il toujours au début sur la ligne ou non?


@Predicate Supposons que tout est une seule ligne, mais vous pouvez trouver plusieurs appels de fonction sur cette ligne: foo (); barre () \ n


est-il possible que les parenthèses ne soient pas équilibrées dans la correspondance souhaitée?


3 Réponses :


2
votes

Votre modèle de groupe e ne fait pas le bon travail, actuellement, il correspond aux parenthèses avec 1 niveau de profondeur car vous n'avez récuré le modèle e qu'une seule fois. Il doit correspondre à autant de sous-chaînes (...) qu'il y en a, et par conséquent, le modèle de sous-programme doit être dans un * ou + code> groupe quantifié, et il peut même être "simplifié" en (? [^ ()] * (?: \ ((? & e) \) [^ ()] *) *) code>.

Notez que votre modèle de groupe e est égal à (? [^ ()] + | \ ((? & e) \)) * code >. [^ ()] * autour de \ ((? & e) \) sont redondants car l'alternative [^ ()] + consommera le caractères autres que ( et ) sur le niveau de profondeur actuel.

De plus, vous avez quantifié le modèle Group e , ce qui en fait un groupe de capture répétée qui ne conserve la correspondance du texte que lors de la dernière itération.

Vous pouvez utiliser

(?<name>\w+)\s*\(\K(?<e>[^()]*(?:\((?&e)\)[^()]*)*)(?=\))

Voir le démo regex

  • (? \ w +) \ s * \ (\ K - 1+ mots de caractères, 0+ espaces blancs et ( qui sont omis de la correspondance
  • (? - début du groupe e
    • [^ ()] * - 0+ caractères autres que ( et )
    • (?: - début d'un groupe non capturant:
      • \ ( - un ( char
      • (? & e) - Motif de groupe e récuré
      • \) - un )
      • [^ ()] * - 0+ caractères autres que ( et )
    • ) * - 0 répétition ou plus
  • ) - fin du groupe e
  • (? = \)) - un ) doit être immédiatement à droite de l'emplacement actuel.

  • 6 commentaires

    semble que la raison pour laquelle l'expression régulière suggérée par OP n'a pas fonctionné était le * immédiatement après le groupe (? , ajoutant un groupe non corrigé à l'intérieur du regex01


    une autre solution plus efficace pour éviter le retour en arrière est d'utiliser un quantificateur possessif par exemple (? \ w +) \ s * \ (\ K (? (?: [^ ()] ++ | \ ( (? & e) \)) *) \)


    @NahuelFouilleul Oui, c'est un peu mieux. Le groupe atomique peut également être utilisé (? \ w +) \ s * \ (\ K (? (?> [^ ()] + | \ ((? & E) \)) * ) (? = \)) . Notez également que le dernier ) doit être dans une anticipation (enfin, au moins c'est la logique OP).


    les gars, la solution échoue sur des parenthèses déséquilibrées. Le peut apparaître sous forme de chaînes.


    @Predicate Ces cas sont hors de portée, ils ne peuvent pas être mis en correspondance avec la récursivité. Ils ne peuvent être traités que si le contexte spécifique est connu aux deux extrémités des correspondances attendues. Vous ne pouvez pas avoir une expression régulière universelle pour ces scénarios.


    @Predicate aussi ";" pourrait apparaître dans les chaînes et votre solution échouera également, mais les chaînes peuvent également être supprimées, en utilisant des verbes de retour arrière, quelque chose comme "(?: [^"] ++ | \\. *) * "(* SKIP) ( ?!) | ...



    0
    votes

    J'ai une expression régulière simple sans récursivité .

    (?<=[\w \n]\()(?:[^;\n"]|(?:"(?:[^"]|\\")*?(?<!\\)"))*(?=\))
    

    maintenant, il traite des perenthesis déséquilibrés, mais il ne traite pas de plusieurs fonctions qui sont sur une seule ligne. Il peut être géré si vous connaissez les délimiteurs entre les fonctions. par exemple. ; s'il s'agit de code Java.

    Variante 2 (mis à jour pour plusieurs fonctions sur une ligne):

    (?<=[\w ]\()([^;\n]|".*?")*(?=\))    
    

    Variante 3 (autorisant ; dans les chaînes):

    (?<=[\w ]\()[^;\n]*(?=\))
    

    Variante 4 (chaînes d'échappement):

    (?<=[\w ]{2}\().*(?=\))
    


    13 commentaires

    vérifier la variante 2


    Vous pouvez améliorer le premier regard en arrière pour qu'il vérifie non seulement le caractère unique, mais le travail devient plus difficile car PCRE n'autorise pas les regards en arrière avec une longueur variable. Il faut probablement plus de contexte pour cela. Mais je ne vois pas de problème à vérifier un seul caractère avant le (


    Et comme * est gourmand, il ira jusqu'au dernier ) et ne correspondra pas à ; également. Le problème d'équilibrage ne se produira pas et plusieurs fonctions sur une ligne sont autorisées (si le délimiteur est ; )


    Soyez bref et simple ^^


    @NahuelFouilleul voir la variante 3


    la variante 3 échouera maintenant avec f ("a;"); g ("b"); , un correctif pourrait être (? <= [\ w] \ () ([^ "; \ n] |" (?: [^ "] ++ | \\. *) *" (* SKIP)) * (? = \))


    Sans récursions, cela ne garantira jamais des correspondances correctes.


    @NahuelFouilleul Je l'ai corrigé avec paresse.)) Maintenant ça marche. Vérifiez Var 3. De plus, votre regex échoue sur la dernière


    Les gars, cmon, j'aime vraiment vos rgex réfléchis, mais je désire au moins quelques goûts de mon code. ^^ Vous dites que vous ne pouvez pas garantir avec mon code, mais apparemment mon code résout tous les cas que nous avons tous créés ensemble et il a fait plus vite que vous codes . ^^


    @NahuelFouilleul conquise dans le Var 4. ^^


    enfin, f ("a") {""}; g ("\" b; "); ou simplement f (" a ") + f (" b "); < / code>


    @revo, comme nous l'avons vu dans une récursivité regex supérieure, ne peut pas non plus le garantir.) Nevermind, j'aime vos solutions les gars, mais j'aime vraiment la simplicité de mon regex


    La quatrième expression régulière échoue sur de nombreuses bonnes correspondances, par exemple. bar (a = ")", b = ");") . C'est un patching sans fin. Vous devez utiliser des récursions pour cela. BTW, les récursions peuvent garantir.



    1
    votes

    L'expression régulière suivante effectue la mise en correspondance sans effectuer d'étapes supplémentaires:

    (?<name>\w+)\s*(\((?<e>([^()'"]*+|"(?>[^"\\]*+|\\.)*"|'(?>[^'\\]*+|\\.)*'|(?2))+)\))
    

    Voir démo en direct ici

    Mais cela ne correspond pas aux chaînes suivantes qui contiennent des parenthèses non équilibrées dans une chaîne entre guillemets:

    • foo (bar = ')')
    • foo (bar (john = "(Doe ..."))

    Vous devez donc rechercher:

    (?<name>\w+)\s*(\((?<e>([^()]*+|(?2))+)\))
    

    Voir démo en direct ici

    Répartition des expressions régulières:

    • (? \ w +) \ s * Faire correspondre le nom de la fonction et les espaces de fin
    • ( Début d'un cluster
      • \ ( Correspond à un littéral (
      • (? Début du groupe de capture nommé e
        • ( Début du groupe de capture n ° 2
          • [^ () '"] * + Correspond à n'importe quoi sauf ()'"
          • | Ou
          • "(?> [^" \\] * + | \\.) * " Correspond à tout élément entre guillemets
          • | Ou
          • '(?> [^' \\] * + | \\.) * ' Correspond à tout élément entre guillemets simples
          • | Ou
          • (? 2) Recurse deuxième groupe de capture
        • ) + Répéter autant que possible, au moins une fois
      • ) Fin du groupe de capture
      • \) Match ) littéralement
    • ) Fin du groupe de capture


    0 commentaires