12
votes

Comment puis-je extraire une corde entre les accolades correspondantes dans Perl?

Mon fichier d'entrée est comme ci-dessous:

$array[0] = "{ABC|*|DEF {GHI 0 1 0} {{Points {}}}}"

$array[1] = "{ABC|*|DEF {GHI 0 2 0} {{Points {}}}}"

$array[2] = "{ABC|*|XYZ:abc:def {GHI 0 22 0} {{Points {{F1 1.1} {F2 1.2} {F3 1.3} {F4 1.4}}}}}"

..
..

$array[5] = "{
    ABC|*|XYZ:abc:pqr {GHI 0 68 0}
        {{Points {{F1 11.11} {F2 12.10} {F3 14.11} {F4 16.23}}}}
        }"


0 commentaires

7 Réponses :


0
votes

Vous êtes beaucoup mieux à l'aide d'une machine à états que d'une regex pour ce type d'analyse.


0 commentaires

2
votes

Je ne pense pas que les expressions régulières pures sont ce que vous voulez utiliser ici (IMHO Cela pourrait même pas être analogue à l'aide de RegEx).

Au lieu de cela, construisez un petit analyseur, similaire à ce qui est montré ici: http: //www.perlmonks .org /? nœud_id = 308039 (Voir la réponse de Shotgunefx (Parson) le 18 novembre 2003 à 18:29 UTC)

mise à jour Il semble que cela puisse être faisable avec une regex - j'ai vu une référence à des parenthèses imbriquées dans Maîtriser des expressions régulières (qui est disponible sur Google Books et peut donc être googlé pour si vous n'avez pas le livre - voir chapitre 5, section" Ensembles équilibrés assortis de parenthèses ")


0 commentaires

0
votes

Les expressions régulières sont réellement mauvaises pour assortir les accolades. En fonction de la profondeur que vous voulez aller, vous pouvez écrire une grammaire complète (qui est beaucoup plus facile que cela ne semble!) Pour Parse :: Recdescent . Ou, si vous voulez simplement obtenir les blocs, recherchez pour ouvrir des marques «{» et la fermeture '}', et gardez simplement le nombre de comptés du nombre de personnes ouvertes à tout moment.


1 commentaires

Merci Zig, votre réponse est très utile.



15
votes

2 commentaires

Merci ysth, c'est la meilleure solution !!


@Srilesh: Si vous aimez la réponse à cette réponse, veuillez cliquer sur la coche décrite à gauche de la réponse.



15
votes

Ceci peut certainement être fait avec regex au moins dans les versions modernes de Perl: xxx pré>

La regex correspond à un bloc d'attelle bouclé contenant soit des caractères sans bouclée, soit une récursivité en elle-même ( Correspond à des bretelles imbriquées) p>

Edit: Le code ci-dessus fonctionne dans Perl 5.10+, pour les versions antérieures, la récursivité est un peu plus verbeuse: p>

my $re; $re = qr/ \{ (?: [^{}]* | (??{$re}) )* \} /x;

my @array = $str =~ /$re/xg;


3 commentaires

Essayé cela, mais je reçois la séquence d'erreur (? 0 ...) non reconnue dans Regex; marqué par <- ici dans m / (\ {(?: [^ {}] * | (? 0 <- ici)) * \}) /


@SRILESH => Le code i Publié précédemment Perl 5.10+, j'ai modifié ma réponse pour inclure une version qui fonctionnera dans les personnes âgées.


Solutions fournies par @ysth, @zaid, @leonbloy fonctionne bien pour moi, mais @ @ Eric's Solution a une très bonne performance. J'applique la récursive sur un fichier de 10 Mo et le résultat est vraiment rapide que les autres. Choisir votre réponse pour être la meilleure solution ici. Merci beaucoup.



2
votes

Vous pouvez toujours compter les accolades:

my $depth = 0;
my $out = "";
my @list=();
foreach my $fr (split(/([{}])/,$data)) {
    $out .= $fr;
    if($fr eq '{') {
        $depth ++;
    }
    elsif($fr eq '}') {
        $depth --;
        if($depth ==0) {
            $out =~ s/^.*?({.*}).*$/$1/s; # trim
            push @list, $out;
            $out = "";
        }
    }
}
print join("\n==================\n",@list);


0 commentaires

4
votes

I Deuxième suggestion de Ysth d'utiliser le Texte :: équilibré code > module. Quelques lignes vous mèneront sur votre chemin. XXX PRE>


SORTIE H3>
{ABC|*|DEF {GHI 0 1 0} {{Points {}}}}
{ABC|*|DEF {GHI 0 2 0} {{Points {}}}}
{ABC|*|XYZ:abc:def {GHI 0 22 0} {{Points {{F1 1.1} {F2 1.2} {F3 1.3} {F4 1.4}}}}}
{ABC|*|XYZ:ghi:jkl {JKL 0 372 0} {{Points {}}}}
{ABC|*|XYZ:mno:pqr {GHI 0 34 0} {{Points {}}}}
{
    ABC|*|XYZ:abc:pqr {GHI 0 68 0}
        {{Points {{F1 11.11} {F2 12.10} {F3 14.11} {F4 16.23}}}}
        }


1 commentaires

Basé sur la suggestion de Ysth, j'ai utilisé du texte :: équilibré, mais je n'obtiens que le premier match. Merci de m'avoir aidé ici, j'ai besoin d'utiliser l'extrait_multiple sous aussi. Merci ..