7 Stimmen

Zeichenkette an ein Muster in Perl ausrichten?

Ich habe Teile von Zeichenketten in eckigen Klammern, etwa so:

[p1 text1/label1] [p2 text2/label2] [p3 text3/label3] [...

und so weiter.

Was sich in den einzelnen Chunks befindet, ist nicht wichtig. Aber manchmal gibt es Textabschnitte, die NICHT von eckigen Klammern umgeben sind. Zum Beispiel:

[p1 text1/label1] [p2 text2/label2] textX/labelX  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]

Ich dachte, ich hätte das mit Regex in Perl gut gelöst, bis mir klar wurde, dass ich nur die Fälle berücksichtigt habe, in denen ein einziger fehlerhafter Text am Anfang, in der Mitte oder am Ende des Textes steht, nicht aber die Fälle, in denen wir zwei fehlerhafte Texte zusammen haben. (wie die Y- und Z-Blöcke oben).

Also habe ich festgestellt, dass reguläre Ausdrücke in Perl nur das erste übereinstimmende Muster abfangen? Wie könnte das obige Problem dann gelöst werden?

Edita:

Das Problem besteht darin, sicherzustellen, dass alle sollte sein von Klammern umgeben. Eckige Klammern sind niemals rekursiv. Wenn eine Phrase mit Klammern umgeben ist, hängt der p-Wert vom "label"-Wert ab. Wenn z. B. eine nicht eingeklammerte Phrase lautet

li/IN

dann sollte es sich in:

[PP li/IN]

Ich schätze, es ist eine Mischung, aber die einzige Möglichkeit, die ich mir vorstellen kann, um das größere Problem zu lösen, an dem ich arbeite, ist, sie alle in Klammerausdrücke zu verwandeln, damit die Handhabung einfacher ist. Es funktioniert also, wenn eine Phrase ohne Klammern am Anfang, in der Mitte und am Ende vorkommt, aber nicht, wenn zwei oder mehr zusammen vorkommen.

Ich habe grundsätzlich für jede Position (Anfang, Mitte und Ende) eine andere Regex verwendet. Die Regex, die eine Phrase ohne Klammern in der Mitte abfängt, sieht so aus:

$data =~ s/\] (text)#\/label \[/\] \[selected-p-value $1#\/label\] \[/g;

Ich stelle also nur fest, dass, wenn ein ] vor und nach dem Text/Label-Muster steht, dieses keine Klammern hat. Ich mache etwas Ähnliches auch für die anderen. Aber ich schätze, das ist unglaublich un-generisch. Meine Regex ist nicht toll!

5voto

canavanin Punkte 2419
#!/usr/bin/perl

use strict;
use warnings;

my $string = "[p1 text1/label1] [p2 text2/label2] textX/labelX  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]";

# don't split inside the [], i.e. not at blanks that have p\d in front of them
my @items = split(/(?<!p\d)\s+/, $string);
my @new_items;

# modify the items that are not inside []
@new_items = map { ($_ =~ m/\[/) ? $_ :
                    ((split("/",$_))[1] eq ("IN")) ? "[PP $_]" :
                    "[BLA $_]";
                 } @items;

print join(' ', @new_items), "\n";

Dies ergibt

[p1 text1/label1] [p2 text2/label2] [PP textX/labelX] [p3 text3/label3] [...] [PP textY/labelY] [PP textZ/labelZ] [...]

Ich nahm an, dass PP war so gemeint, wie ich es hier verwendet habe, sonst wäre die map muss etwas aufwändiger werden.

EDITAR

Ich habe den Code als Antwort auf Ihren Kommentar bearbeitet. Wenn Sie verwenden

"[p1 text1/label1] [p2 text2/label2] textX/IN  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]";

als Beispielzeichenkette, so sieht die Ausgabe aus:

[p1 text1/label1] [p2 text2/label2] [PP textX/IN] [p3 text3/label3] [...] [BLA textY/labelY] [BLA textZ/labelZ] [...]

Nur eine Sache sollte man bedenken: Die Regex, die mit split wird nicht funktionieren bei pn mit n > 9. Wenn Sie solche Fälle haben, suchen Sie am besten nach einer Alternative, da Lookbehinds mit variabler Länge nicht implementiert wurden (oder zumindest in meiner Version von Perl (5.10.1) nicht).

EDIT 2

Als Antwort auf Ihren zweiten Kommentar finden Sie hier eine modifizierte Version des Skripts. Sie werden feststellen, dass ich auch etwas zu der Beispielzeichenkette hinzugefügt habe, um zu zeigen, dass es jetzt auch funktioniert, wenn es keine pn innerhalb der [...] .

#!/usr/bin/perl

use strict;
use warnings;

my $string = "[p1 text1/label1] [p2 text2/label2] textX/IN  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...] xyx/IN [opq rs/abc]";

# we're using a non-greedy match to only capture the contents of one set of [], 
# otherwise we'd simply match everything between the first [ and the last ].
# The parentheses around the match ensure that our delimiter is KEPT.
my @items = split(/(\[.+?\])/, $string);

#print "..$_--\n" for @items;  # uncomment this to see what the split result looks like

# modify the items that are not inside []
my @new_items = map {
                     if (/^\[/) { # items in []
                        $_;
                     }
                     elsif (/(?: \w)|(?:\w )/) { # an arbitrary number of items without []
                       my @new =  map { ($_ =~ m/\[/) ? $_ :
                                        ((split("/",$_))[1] eq ("IN")) ? "[PP $_]" :
                                        "[BLA $_]";
                                      } split;
                     }
                     else { # some items are '', let's just discard those
                     }
                    } @items;

print join(' ', @new_items), "\n";

Das Ergebnis ist dieses:

[p1 text1/label1] [p2 text2/label2] [PP textX/IN] [p3 text3/label3] [...] [BLA textY/labelY] [BLA textZ/labelZ] [...] [PP xyx/IN] [opq rs/abc]

Ich habe festgestellt, dass Sie bereits die gewünschte Hilfe erhalten haben, aber ich dachte, ich könnte Ihre Frage trotzdem beantworten...

2voto

parapura rajkumar Punkte 23657

Sie haben Ihren regulären Ausdruck nicht mitgeteilt, aber Sie sollten die g für Global Replace. Andernfalls ersetzen reguläre Perl-Ausdrücke nur die erste Übereinstimmung

my $teststring = "hello world";

$teststring =~ s/o/X/;

werden hellX world . aber

$teststring =~ s/o/X/g;

werden hellX wXrld alle Spiele zu bemerken.

Ich denke, Ihr Problem ist ungefähr so

my $teststring = ' A B C ';

$teststring =~ s/\s(\w)\s/ [$1] /ig;

ergibt [A] B [C] . Der Grund dafür ist, dass sie im Rahmen der Anpassung A die Regex-Maschine hat auch das Leerzeichen nach A verbraucht. Und in der verbleibenden Zeichenfolge gibt es kein Leerzeichen vor B, so dass sie nicht übereinstimmt.

Wenn Sie jedoch ein nicht gieriges Spiel wie folgt durchführen

$teststring =~ s/\s(\w)\s*?/ [$1] /ig;

es ergibt sich [A] [B] [C]

2voto

FailedDev Punkte 25987

Eigentlich kann man das Problem lösen, indem man "nur" regex :

#!/usr/bin/perl

use strict;
use warnings;

$_ = "[p1 text1/label1] [p2 text2/label2] textX/labelX  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]";

s{ ([^\s[]+)|(\[(?:[^[]*)\])     }
 { if( defined $2){ $2 } elsif(defined $1)
    { 
       if($1 =~ m!(.*(?<=/)(.*))!)
       {
         if($2 eq 'labelX')
         {
            "[PP $1]";
         }
         elsif($2 eq 'labelY')
         {
            "[BLA $1]";
         }
         elsif($2 eq 'labelZ')
         {
            "[FOO $1]";
         }
       }
    }
 }xge;

 print;

Ausgang :

[p1 text1/label1] [p2 text2/label2] [PP textX/labelX]  [p3 text3/label3] [...] [BLA textY/labelY] [FOO textZ/labelZ] [...]

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X