2009-05-20 1 views
0

My stringSuchen und Ersetzen-Muster in einem String

(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))] [] []). 

(Zur besseren Lesbarkeit Kommentiert):

(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) 
    )] 
    [] 
    [] 
). 

In der obigen Zeichenfolge, ich mag die ganzzahlige Werte ersetzen, die jeweils durch diese Werte :

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

ich habe 8 Werte in der Zeichenfolge zu ersetzen.

Das ist mein 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; 

Es ist nicht die Werte überhaupt nicht ersetzen; Kann mir bitte jemand helfen? Vielen Dank.

+0

Hinweis: Sie haben nur 5 geklammerten Gruppen in Ihrem Muster, aber Sie verwenden Rückreferenzierungen $ 6 9 $. Warum? –

+0

Ich kann dein Problem nicht reproduzieren: Auch bei den zusätzlichen Rückreferenzen funktioniert die Ersetzung für mich. Erhalten Sie Fehler- oder Warnmeldungen? –

+0

Hallo Nathan, ich habe 8 Werte in der Zeichenfolge zu ersetzen. – user105265

Antwort

1

dies für Größe ausprobieren:

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; 

, das eine Liste macht, @temp, auf der Grundlage der "Wörter" (ungefähr) in $ligne. Es erstellt eine andere Liste, @numbers, die eine Liste der Nummern darstellt, die Sie in der Liste ersetzen möchten, in der Reihenfolge, in der sie die Dinge ersetzen sollen. Dann geht es durch @temp, eins nach dem anderen, und wenn ein gegebenes Element eine Zahl ist (dh entspricht der Regex /^\W*\d\W*$/, was bedeutet, dass es keine Wortzeichen (also nicht "champs4") hat und mindestens eine Zahl hat - das wird "25346)" zusätzlich zu "25346"), und ersetzen Sie dann den numerischen Teil durch den ersten Wert von @numbers. Und jetzt, nachdem ich es getestet habe, kann ich Ihnen versichern, dass das tatsächlich funktioniert!

Ich glaube, eine kürzere Implementierung könnte mit map erreicht werden, aber das wird gut genug für Sie arbeiten.

Vorteile dieses Ansatzes zu Ihrem Ansatz:

Zuerst diese Lösung ist skalierbar. Um mehr als acht Zahlen mit Ihrer Lösung zu ersetzen, müssten Sie eine neue Regex schreiben. Um mehr als acht Zahlen durch meine Lösung zu ersetzen, fügen Sie einfach weitere Einträge zu @numbers hinzu. Dieser Code könnte in eine Subroutine eingefügt werden, die eine zu ändernde Zeichenfolge und eine Liste mit zu ändernden Zahlen benötigt, und Sie müssen sich keine Gedanken darüber machen, ob sie die richtige Anzahl von Zahlen übergeben haben oder ob sie das richtige Format haben.

Zweitens ist dies auf den ersten Blick ein bisschen leichter zu verstehen. Regexes, solange das von Ihnen verwendete sehr schwer visuell zu analysieren ist. Selbst wenn es funktioniert, muss vielleicht jemand Ihren Code ändern, um etwas anderes zu tun. Wenn Sie einen riesigen Regex verwenden, wird der Rewriter (vielleicht Sie) einfach den Kopf schütteln, Ihren Code hervorheben und auf Löschen drücken und dann neuen Code schreiben, um es zu tun. Mit diesem können sie leicht sehen, was in Ihrem Code passiert, und wenn sie Änderungen daran vornehmen müssen, können sie.

Drittens, wenn Sie in einer bestimmten Anzahl von Alternativen codieren wollen, machen Sie das auch tun können:

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; 

Als Randnotiz (die früher angewandt, gilt aber nach wie vor), wird dies scheitern auf Fließkommazahlen - /^\W*\d\W*$/ wird Fließkommazahlen entsprechen, aber s/\d+/$num/ wird keine Fließkommazahlen ersetzen, nur der ganzzahlige Teil.Wenn Sie feststellen, müssen Sie Gleitkommazahlen, ändern Sie diese Zeile:

s/\d+/$num/; 

Um dies:

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

Das Gleitkommazahlen übereinstimmen sollte.

+0

Vielen Dank an alle. Chris Lutz, deine Lösung ersetzt nur den ersten Wert nach dem Debüt, und den ersten Wert nach dem Fin, und so weiter für das andere Debüt und Fin, also nur 4 Werte. – user105265

+0

Tatsächlich tut es das. Das ist es, was ich bekomme, bevor ich den Code nicht getestet habe. Wie auch immer, es vermisst die zweite Nummer, weil es "25345)" anstelle von "25345" lautet, und das ")" stimmt nicht mit einer Zahl überein. Dies wird schnell behoben. –

0

Sie scheinen es das Gegenteil zu tun, dass ich würde. ich würde nach den Zahlen suchen und diese ersetzen, anstatt zu entscheiden, was du donnest, d. h., die Daten um die Zahlen herum zu finden und sie in eine Zeichenkette zu ersetzen.

Wird es IMMER 8 Werte geben? Werden sie immer denselben Worten folgen? wenn ja:

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

oder debütieren kann & Finne überall erscheinen, und wann immer sie wollen Sie sie als solche ersetzen:

Debüt xy -> Debüt $ moyLargRectNom, $ moyHautRectNom, fin xy -> fin $ moyLargRectNom, $ moyHautRectNom, (Debüt 144825 25345) (fin 244102 40647)

wenn das wahr ist, kann es nur mit zwei einfachen Regex:

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

und ersetzen Sie die Gruppen durch die Wörter ..

aber ich kann mich nicht erinnern, welche Variable speichert die Anzahl der Gruppen erstellt, sorry.

1

sprintf zur Rettung:

#!/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

ich Ihre Struktur figgured war zu unregelmäßig oder seltsam für einen regulären Ausdruck passen, verschachtelte Ausdrücke selten sind.

Also ging ich auf die Jagd nach einem Parse-Baum. Da ich keine passende fand und keine der formalen Grammatiken verstand, schrieb ich meinen eigenen Tokenster/Zustandsautomat.

Es verwandelt Ihren Code in einen Datenbaum, den Sie dann mit einfachen Schleifenkonstrukten extrahieren können.

Vorsicht,, Code ist nur entwickelt, um auf Ihrem kleinen Datensatz so weit zu arbeiten, unausgeglichene Klammern geben Parser Kopfschmerzen und produzieren einen nutzlosen Baum.

Skim nach unten, um zu sehen, wie dieses 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]); 
} 

Ausgabe verwenden:

(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' 
    ] 
} 
Verwandte Themen