web-dev-qa-db-fra.com

Expression régulière pour correspondre aux crochets extérieurs

J'ai besoin d'une expression régulière pour sélectionner tout le texte entre deux crochets externes.

Exemple: some text(text here(possible text)text(possible text(more text)))end text

Résultat: (text here(possible text)text(possible text(more text)))

222
DaveF

Les expressions régulières ne sont pas le bon outil pour le travail, car vous avez affaire à des structures imbriquées, c'est-à-dire à la récursivité.

Mais il existe un algorithme simple pour le faire, que j'ai décrit dans cette réponse à une question précédente

119
Frank

Vous pouvez utiliser regex récursion :

\(([^()]|(?R))*\)
82
rogal111
[^\(]*(\(.*\))[^\)]*

[^\(]* correspond à tout ce qui n'est pas un crochet d'ouverture au début de la chaîne, (\(.*\)) capture la sous-chaîne requise entre crochets et [^\)]* correspond à tout ce qui n'est pas un crochet de fermeture à la fin de la chaîne. Notez que cette expression ne tente pas de faire correspondre les crochets; un simple analyseur (voir la réponse de dehmann ) conviendrait mieux pour cela. 

26
Zach Scrivena
(?<=\().*(?=\))

Si vous souhaitez sélectionner du texte entre deux parenthèses correspondantes, vous n'avez pas de chance avec les expressions régulières. C'est impossible(*).

Cette expression rationnelle ne fait que renvoyer le texte entre la première et la dernière parenthèse de fermeture de votre chaîne.


(*) Sauf si votre moteur regex a des fonctionnalités telles que groupes d'équilibrage ou récursion . Le nombre de moteurs prenant en charge de telles fonctionnalités augmente lentement, mais ils ne sont pas encore disponibles. 

14
Tomalak

Il est en fait possible de le faire en utilisant des expressions régulières .NET, mais ce n’est pas anodin, alors lisez attentivement. 

Vous pouvez lire un article de Nice ici . Vous devrez peut-être également lire les expressions régulières .NET. Vous pouvez commencer à lire ici .

Les crochets <> ont été utilisés car ils ne nécessitent pas d'échappement.

L'expression régulière ressemble à ceci:

<
[^<>]*
(
    (
        (?<Open><)
        [^<>]*
    )+
    (
        (?<Close-Open>>)
        [^<>]*
    )+
)*
(?(Open)(?!))
>
11
Alexander Bartosh

Cette réponse explique la limitation théorique de la raison pour laquelle les expressions régulières ne sont pas le bon outil pour cette tâche.


Les expressions régulières ne peuvent pas le faire. 

Les expressions régulières sont basées sur un modèle informatique appelé Finite State Automata (FSA). Comme son nom l'indique, une FSA ne peut se souvenir que de l'état actuel, elle ne dispose d'aucune information sur les états précédents.

 FSA

Dans le diagramme ci-dessus, S1 et S2 sont deux états où S1 est l’étape de départ et l’étape finale. Donc, si nous essayons avec la chaîne 0110, la transition se passe comme suit:

      0     1     1     0
-> S1 -> S2 -> S2 -> S2 ->S1

Dans les étapes ci-dessus, lorsque nous sommes au deuxième S2, c'est-à-dire après l'analyse du 01 du 0110, la FSA ne dispose d'aucune information sur le 0 précédent dans 01 car il ne peut que mémoriser l'état actuel et le symbole d'entrée suivant.

Dans le problème ci-dessus, nous devons connaître le non de l'ouverture de la parenthèse; cela signifie qu'il doit être stocké à un endroit donné. Mais comme FSAs ne peut pas faire cela, une expression régulière ne peut pas être écrite.

Cependant, un algorithme peut être écrit pour atteindre l'objectif. Les algorithmes sont généralement tombés sous Pushdown Automata (PDA). PDA est un niveau supérieur à FSA. PDA a une pile supplémentaire pour stocker quelque chose. Les PDA peuvent être utilisés pour résoudre le problème ci-dessus, car nous pouvons «Push» ouvrir la parenthèse dans la pile et «pop» dès que nous rencontrons une parenthèse fermante. Si à la fin, la pile est vide, l'ouverture entre parenthèses et la fermeture des parenthèses correspond. Sinon non. 

Une discussion détaillée peut être trouvée ici

4
Somnath Musib

C'est la regex définitive:

\(
(?<arguments> 
(  
  ([^\(\)']*) |  
  (\([^\(\)']*\)) |
  '(.*?)'

)*
)
\)

Exemple:

input: ( arg1, arg2, arg3, (arg4), '(pip' )

output: arg1, arg2, arg3, (arg4), '(pip'

notez que le '(pip' est correctement géré en tant que chaîne . (essayé dans le régulateur: http://sourceforge.net/projects/regulator/

3
Marco

L'expression régulière utilisant Ruby (version 1.9.3 ou supérieure):

/(?<match>\((?:\g<match>|[^()]++)*\))/

Démo sur rubular

2
Joy Hu

Vous avez besoin de la première et de la dernière parenthèses. Utilisez quelque chose comme ceci:

str.indexOf ('('); - cela vous donnera la première occurrence

str.lastIndexOf (')'); - le dernier

Donc, vous avez besoin d'une chaîne entre, 

String searchedString = str.substring(str1.indexOf('('),str1.lastIndexOf(')');
2
Shell Scott

J'ai écrit une petite bibliothèque JavaScript appelée balanced pour vous aider dans cette tâche. Vous pouvez accomplir cela en faisant

balanced.matches({
    source: source,
    open: '(',
    close: ')'
});

Vous pouvez même faire des remplacements:

balanced.replacements({
    source: source,
    open: '(',
    close: ')',
    replace: function (source, head, tail) {
        return head + source + tail;
    }
});

Voici un exemple plus complexe et interactif JSFiddle .

2
Chad Scira

Voici une solution personnalisable permettant des délimiteurs de littéraux à caractère unique en Java:

public static List<String> getBalancedSubstrings(String s, Character markStart, 
                                 Character markEnd, Boolean includeMarkers) 

{
        List<String> subTreeList = new ArrayList<String>();
        int level = 0;
        int lastOpenDelimiter = -1;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == markStart) {
                level++;
                if (level == 1) {
                    lastOpenDelimiter = (includeMarkers ? i : i + 1);
                }
            }
            else if (c == markEnd) {
                if (level == 1) {
                    subTreeList.add(s.substring(lastOpenDelimiter, (includeMarkers ? i + 1 : i)));
                }
                if (level > 0) level--;
            }
        }
        return subTreeList;
    }
}

Exemple d'utilisation:

String s = "some text(text here(possible text)text(possible text(more text)))end text";
List<String> balanced = getBalancedSubstrings(s, '(', ')', true);
System.out.println("Balanced substrings:\n" + balanced);
// => [(text here(possible text)text(possible text(more text)))]
1

Celui-ci a également travaillé

re.findall(r'\(.+\)', s)
0
DataScienceStep

La réponse dépend de la nécessité de faire correspondre les ensembles de crochets correspondants ou simplement du premier ouvert à la dernière clôture du texte saisi.

Si vous devez faire correspondre les crochets imbriqués, vous avez besoin de plus que des expressions régulières. - voir @dehmann

S'il est juste ouvert pour la dernière fois, voir @Zach

Décidez ce que vous voulez qu'il se passe avec:

abc ( 123 ( foobar ) def ) xyz ) ghij

Vous devez décider à quoi votre code doit correspondre dans ce cas.

0
Douglas Leeder

Cela peut être utile à certains:

Analyser les paramètres de la chaîne de fonction (avec des structures imbriquées) en javascript

Match des structures comme:
 Parse parmeters from function string 

  • correspond aux crochets, crochets, parenthèses, guillemets simples et doubles

Ici vous pouvez voir l'expression rationnelle générée en action

/**
 * get param content of function string.
 * only params string should be provided without parentheses
 * WORK even if some/all params are not set
 * @return [param1, param2, param3]
 */
exports.getParamsSAFE = (str, nbParams = 3) => {
    const nextParamReg = /^\s*((?:(?:['"([{](?:[^'"()[\]{}]*?|['"([{](?:[^'"()[\]{}]*?|['"([{][^'"()[\]{}]*?['")}\]])*?['")}\]])*?['")}\]])|[^,])*?)\s*(?:,|$)/;
    const params = [];
    while (str.length) { // this is to avoid a BIG performance issue in javascript regexp engine
        str = str.replace(nextParamReg, (full, p1) => {
            params.Push(p1);
            return '';
        });
    }
    return params;
};

Ceci ne répond pas complètement à la question OP mais je pense que cela pourrait être utile à certains qui viennent ici de chercher une structure imbriquée regexp.

0
538ROMEO
"""
Here is a simple python program showing how to use regular
expressions to write a paren-matching recursive parser.

This parser recognises items enclosed by parens, brackets,
braces and <> symbols, but is adaptable to any set of
open/close patterns.  This is where the re package greatly
assists in parsing. 
"""

import re


# The pattern below recognises a sequence consisting of:
#    1. Any characters not in the set of open/close strings.
#    2. One of the open/close strings.
#    3. The remainder of the string.
# 
# There is no reason the opening pattern can't be the
# same as the closing pattern, so quoted strings can
# be included.  However quotes are not ignored inside
# quotes.  More logic is needed for that....


pat = re.compile("""
    ( .*? )
    ( \( | \) | \[ | \] | \{ | \} | \< | \> |
                           \' | \" | BEGIN | END | $ )
    ( .* )
    """, re.X)

# The keys to the dictionary below are the opening strings,
# and the values are the corresponding closing strings.
# For example "(" is an opening string and ")" is its
# closing string.

matching = { "(" : ")",
             "[" : "]",
             "{" : "}",
             "<" : ">",
             '"' : '"',
             "'" : "'",
             "BEGIN" : "END" }

# The procedure below matches string s and returns a
# recursive list matching the nesting of the open/close
# patterns in s.

def matchnested(s, term=""):
    lst = []
    while True:
        m = pat.match(s)

        if m.group(1) != "":
            lst.append(m.group(1))

        if m.group(2) == term:
            return lst, m.group(3)

        if m.group(2) in matching:
            item, s = matchnested(m.group(3), matching[m.group(2)])
            lst.append(m.group(2))
            lst.append(item)
            lst.append(matching[m.group(2)])
        else:
            raise ValueError("After <<%s %s>> expected %s not %s" %
                             (lst, s, term, m.group(2)))

# Unit test.

if __== "__main__":
    for s in ("simple string",
              """ "double quote" """,
              """ 'single quote' """,
              "one'two'three'four'five'six'seven",
              "one(two(three(four)five)six)seven",
              "one(two(three)four)five(six(seven)eight)nine",
              "one(two)three[four]five{six}seven<eight>nine",
              "one(two[three{four<five>six}seven]eight)nine",
              "oneBEGINtwo(threeBEGINfourENDfive)sixENDseven",
              "ERROR testing ((( mismatched ))] parens"):
        print "\ninput", s
        try:
            lst, s = matchnested(s)
            print "output", lst
        except ValueError as e:
            print str(e)
    print "done"
0
Gene Olson