2009-05-20 10 views
0

Ma chaîneRechercher et remplacer les modèles dans une chaîne

(champs1 (champs6 donnee_o donnee_f) [(champs2 [] (champs3 _YOJNJeyyyyyyB (champs4 donnee_x)) (debut 144825 25345) (fin 244102 40647)), (champs2 [] (champs3 _FuGNJeyyyyyyB (champs4 donnee_z)) (debut 796443 190570) (fin 145247 42663))] [] []). 

(annotés Pour plus de lisibilité):

(champs1 
    (champs6 donnee_o donnee_f) 
    [(champs2 [] 
      (champs3 _YOJNJeyyyyyyB (champs4 donnee_x)) 
      (debut 144825 25345) 
      (fin 244102 40647) 
     ), 
     (champs2 [] 
      (champs3 _FuGNJeyyyyyyB (champs4 donnee_z)) 
      (debut 796443 190570) 
      (fin 145247 42663) 
    )] 
    [] 
    [] 
). 

Dans la chaîne ci-dessus, je voudrais remplacer les valeurs entières, respectivement par ces valeurs :

$moyLargRectNom, $moyHautRectNom, $moyLargRectNom, 
$moyHautRectNom, $moyLargRectMat, $moyHautRectMat, 
$moyLargRectMat, $moyHautRectMat 

J'ai 8 valeurs à remplacer dans la chaîne.

Ceci est mon REGEX

$ligne =~ s{ 
    (.*debut) \s\d+ \s\d+ 
    (.*fin) \s\d+ \s\d+ 
    (.*debut) \s\d+ \s\d+ 
    (.*fin) \s\d+ \s\d+ 
    (.*) 
}{ 
    $1 . $moyLargRectNom . 
    $2 . $moyHautRectNom . 
    $3 . $moyLargRectNom . 
    $4 . $moyHautRectNom . 
    $5 . $moyLargRectMat . 
    $6 . $moyHautRectMat . 
    $7 . $moyLargRectMat . 
    $8 . $moyHautRectMat . 
    $9 
}xe; 

Il ne remplace pas les valeurs du tout; Quelqu'un peut-il m'aider s'il-vous-plaît? Je vous remercie.

+0

Note: Vous avez seulement 5 groupes parenthésées dans votre modèle, mais vous utilisez des backreferences 6 $ pour 9 $. Pourquoi? –

+0

Je ne peux pas reproduire votre problème: Même avec les backreferences supplémentaires, la substitution fonctionne pour moi. Recevez-vous des messages d'erreur ou d'avertissement? –

+0

Salut Nathan, j'ai 8 valeurs à remplacer dans la chaîne. – user105265

Répondre

1

Essayez ceci pour la taille:

my @numbers = ($moyLargRectNom, $moyHautRectNom, $moyLargRectNom, $moyHautRectNom, $moyLargRectMat, $moyHautRectMat, $moyLargRectMat, $moyHautRectMat); 
my @temp = split//, $ligne; 
for(@temp) { 
    if(/^\W*\d\W*$/) { 
    my $num = shift @numbers; 
    s/\d+/$num/; 
    } 
} 
$ligne = join " ", @temp; 

Cela fait une liste, @temp, sur la base des "mots" (environ) à $ligne. Il fait une autre liste, @numbers, qui est une liste des numéros que vous voulez remplacer dans la liste, dans l'ordre où vous voulez qu'ils remplacent les choses. Ensuite, il passe par @temp, un par un, et si un élément donné est un nombre (ie correspond à la regex /^\W*\d\W*$/, ce qui signifie qu'il n'a pas de caractères de mot (donc ce n'est pas "champs4") et a au moins un nombre - ceci correspondra à "25346)" en plus de "25346"), puis remplacez la partie numérique par la première valeur de @numbers. Et maintenant que je l'ai testé, je peux vous assurer que cela fonctionne réellement!

Je crois qu'une implémentation plus courte pourrait être réalisée avec map, mais cela fonctionnera assez bien pour vous.

Les avantages de cette approche à votre approche:

En premier lieu, cette solution est évolutive. Pour remplacer plus de huit numéros avec votre solution, vous devez écrire une nouvelle expression rationnelle. Pour remplacer plus de huit nombres avec ma solution, ajoutez juste quelques autres entrées à @numbers. Ce code pourrait être mis dans un sous-programme qui prend une chaîne à changer et une liste de nombres à changer, et vous n'aurez pas à vous soucier de savoir s'ils ont passé le bon nombre de nombres ou s'ils ont le bon format. Deuxièmement, c'est un peu plus facile à comprendre au premier coup d'œil. Les expressions régulières, aussi longues que celle que vous utilisiez, sont très difficiles à analyser visuellement. Même si cela fonctionne, un jour quelqu'un peut avoir besoin de modifier votre code pour faire quelque chose de différent. Si vous utilisez une regex énorme, le réécriteur (peut-être vous) va simplement secouer la tête, mettre en évidence votre code, et appuyez sur supprimer, puis écrire un nouveau code pour le faire. Avec cela, ils peuvent facilement voir ce qui se passe dans votre code, et s'ils ont besoin d'y apporter des modifications, ils peuvent le faire.

Troisièmement, si vous voulez hardcode dans un certain nombre de remplacements à faire, vous pouvez le faire aussi:

my @numbers = ($moyLargRectNom, $moyHautRectNom, $moyLargRectNom, $moyHautRectNom, $moyLargRectMat, $moyHautRectMat, $moyLargRectMat, $moyHautRectMat); 
my @temp = split//, $ligne; 
my $max_replacements = 8; 
for(@temp) { 
    if(/^\W*\d\W*$/) { 
    my $num = shift @numbers; 
    s/\d+/$num/; 
    last unless --$max_replacements; 
    } 
} 
$ligne = join " ", @temp; 

Comme une note latérale (qui appliqué plus tôt, mais applique encore), cela ne fonctionne pas sur les nombres à virgule flottante - /^\W*\d\W*$/ correspondra aux nombres à virgule flottante, mais s/\d+/$num/ ne remplacera pas les nombres à virgule flottante, seulement la partie entière.Si vous découvrez que vous avez besoin nombres à virgule flottante, changez cette ligne:

s/\d+/$num/; 

à ceci:

s/\d+|(?:\d+)?\.\d+/$num/; 

Cela doit correspondre à un nombre à virgule flottante.

+0

Merci à tous. Chris Lutz, votre solution ne remplace que la première valeur après le début, et la première valeur après la fin, et ainsi de suite pour les autres débuts et fin, donc seulement 4 valeurs. – user105265

+0

En effet, c'est le cas. C'est ce que je reçois pour ne pas tester le code avant de le poster. Quoi qu'il en soit, il manque le deuxième nombre car il lit "25345)" au lieu de "25345", et le ")" ne correspond pas en tant que nombre. Cela sera rapidement rectifié. –

0

Vous semblez le faire à l'opposé de ce que je ferais. c'est-à-dire que je chercherais les chiffres et les remplacerais, plutôt que ce que vous êtes en train de faire, c'est-à-dire, en faisant correspondre les choses entourant les nombres et en les substituant dans une chaîne.

Y aura-t-il TOUJOURS 8 valeurs? Vont-ils toujours suivre les mêmes mots? le cas échéant:

.+?debut\s([\d]+)\s([\d]+).+?fin\s([\d]+)\s([\d]+).+?debut\s([\d]+)\s([\d]+).+?fin\s([\d]+)\s([\d]+) 

ou peut débuter & fin apparaissent partout, et quand ils veulent que vous les remplacer en tant que tels:

débuts xy -> débuts $ moyLargRectNom, moyHautRectNom $, fin xy -> fin $ moyLargRectNom, moyHautRectNom $, (débuts 144825 25345) (fin 244102 40647)

si cela est vrai, juste le faire en utilisant deux regex simple:

debut\s([\d]+)\s([\d]+) 
fin\s([\d]+)\s([\d]+) 

et remplacer les groupes avec les mots ..

mais je ne me souviens pas quelle variable stocke le nombre de groupes créés, désolé.

1

sprintf à la rescousse:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $s = <<EO_TXT; 
(champs1 (champs6 donnee_o donnee_f) [(champs2 [] 
(champs3 _YOJNJeyyyyyyB (champs4 donnee_x)) (debut 144825 25345) 
(fin 244102 40647)), (champs2 [] (champs3 _FuGNJeyyyyyyB 
(champs4 donnee_z)) (debut 796443 190570) (fin 145247 42663))] [] []). 
EO_TXT 

my ( 
    $moyLargRectNom, $moyHautRectNom, 
    $moyLargRectMat, $moyHautRectMat, 
) = map { "val$_" } qw(1 2 3 4); 

my @replacements = (
    $moyLargRectNom, $moyHautRectNom, 
    $moyLargRectNom, $moyHautRectNom, 
    $moyLargRectMat, $moyHautRectMat, 
    $moyLargRectMat, $moyHautRectMat, 
); 

$s =~ s/\b[0-9]+\b/%s/g; # replace %s with the appropriate specifier 
$s = sprintf $s, @replacements; 

print $s, "\n"; 
0

Je figgured votre structure était trop irrégulière ou étrange d'être adapté à une expression régulière, les expressions imbriquées sont rarement. Donc, je suis allé à la chasse d'un arbre d'analyse. Ne trouvant pas celui qui convenait, et ne comprenant aucune des grammaires d'analyse formelles, j'ai écrit mon propre tokenister/machine d'état.

Il transforme votre code en un arbre de données que vous pouvez ensuite extraire avec de simples constructions en boucle.

Méfiez-vous, le code est seulement conçu pour fonctionner sur votre petit ensemble de données fourni jusqu'à présent, les parenthèses déséquilibrées donneront des maux de tête d'analyseur et produiront un arbre inutile.

Skim vers le bas pour voir comment utiliser cette blob

#!/usr/bin/perl 

use strict; 
use warnings; 
use version; 
use Data::Dumper; 
our $VERSION = qv('0.1'); 

my @stack; 

my $data = <<'EOF'; 
(champs1 
    (champs6 donnee_o donnee_f) 
    [(champs2 [] 
      (champs3 _YOJNJeyyyyyyB (champs4 donnee_x)) 
      (debut 144825 25345) 
      (fin 244102 40647) 
     ), 
     (champs2 [] 
      (champs3 _FuGNJeyyyyyyB (champs4 donnee_z)) 
      (debut 796443 190570) 
      (fin 145247 42663) 
    )] 
    [] 
    [] 
) 
EOF 

push @stack, 
    { 
    tokens => [], 
    context => 'void', 
    }; 

my $state; 

my $eaten; 
my $str = $data; 

sub eat 
{ 
    my $n = shift; 
    substr($str, 0, $n, ''); 
} 

while (@stack && $str) 
{ 
    $state = $stack[-1]; 
    my @tokens = @{ $stack[-1]->{tokens} }; 
    my $context = $stack[-1]->{context}; 

    if ($str =~ m{(^[\s,]+)}) 
    { 
     eat length($1); 
     next; 
    } 
    if ($str =~ m{(^\w+)}) 
    { 
     eat length($1); 
     push @{ $stack[-1]->{tokens} }, $1; 
     next; 
    } 
    if ( $str =~ m{^\[} 
     and $context eq 'nest' 
     || $context eq 'nestgroup' 
     || $context eq 'array') 
    { 
     eat 1; 
     print "\e[33m[\e[0m"; 
     push @stack, 
      { 
      tokens => [], 
      context => 'array', 
      }; 

     next; 
    } 

    if ($str =~ m{^\]} and $context eq 'array') 
    { 
     eat 1; 
     print "\e[33m]\e[0m"; 
     pop @stack; 
     push @{ $stack[-1]->{tokens} }, \@tokens; 
     next; 
    } 

    if (
     $str =~ m{^\((champs(\d)|debut|fin)\s} 
     and ( $context eq 'nest' 
      || $context eq 'array' 
      || $context eq 'nestgroup' 
      || $context eq 'void') 
    ) 
    { 
     eat length($1) + 1; 
     $stack[-1]->{nodename} = $1; 
     print "\e[32m($1\e[0m"; 
     push @stack, 
      { 
      tokens => [], 
      context => 'nestgroup', 
      }; 
     next; 
    } 
    if ($str =~ m{^\)} and $context eq 'nestgroup') 
    { 
     eat 1; 
     print "\e[32m)\e[0m"; 
     pop @stack; 
     my $nodename = $stack[-1]->{nodename}; 
     push @{ $stack[-1]->{tokens} }, { $nodename, \@tokens }; 
     next; 
    } 
    if ($str =~ m{^\(}) 
    { 
     eat 1; 
     print "\e[31m(\e[0m"; 
     push @stack, 
      { 
      tokens => [], 
      context => 'nest', 
      }; 
     next; 
    } 
    if ($str =~ m{^\)} and $context eq 'nest') 
    { 
     eat 1; 
     print "\e[31m)\e[0m"; 
     pop @stack; 
     push @{ $stack[-1]->{tokens} }, \@tokens; 
     next; 
    } 

    print substr($str, 0, 1), "\e[34m$context\e[0m"; 
    eat 1; 
} 

$Data::Dumper::Indent = 1; 
$Data::Dumper::Terse = 1; 

print "Tree:\n"; 
print Dumper($state->{tokens}->[0]->{champs1}->[1]); 

print "--------"; 
for (@{ $state->{tokens}->[0]->{champs1}->[1] }) 
{ 
    my @data = @{ $_->{champs2} }; 
    print ">", Dumper($data[2], $data[3]); 
} 

Sortie:

(champs1(champs6)[(champs2[](champs3(champs4))(debut)(fin))(champs2[](champs3(champs4))(debut)(fin))][][]) 
Tree: 
[ 
    { 
    'champs2' => [ 
     [], 
     { 
     'champs3' => [ 
      '_YOJNJeyyyyyyB', 
      { 
      'champs4' => [ 
       'donnee_x' 
      ] 
      } 
     ] 
     }, 
     { 
     'debut' => [ 
      '144825', 
      '25345' 
     ] 
     }, 
     { 
     'fin' => [ 
      '244102', 
      '40647' 
     ] 
     } 
    ] 
    }, 
    { 
    'champs2' => [ 
     [], 
     { 
     'champs3' => [ 
      '_FuGNJeyyyyyyB', 
      { 
      'champs4' => [ 
       'donnee_z' 
      ] 
      } 
     ] 
     }, 
     { 
     'debut' => [ 
      '796443', 
      '190570' 
     ] 
     }, 
     { 
     'fin' => [ 
      '145247', 
      '42663' 
     ] 
     } 
    ] 
    } 
] 
-------- 
>{ 
    'debut' => [ 
    '144825', 
    '25345' 
    ] 
} 
{ 
    'fin' => [ 
    '244102', 
    '40647' 
    ] 
} 
>{ 
    'debut' => [ 
    '796443', 
    '190570' 
    ] 
} 
{ 
    'fin' => [ 
    '145247', 
    '42663' 
    ] 
} 
Questions connexes