7 Stimmen

String auf ein Muster in Perl ausrichten?

Ich habe Schnipsel von Zeichenfolgen innerhalb von eckigen Klammern, so wie hier:

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

und so weiter.

Was sich innerhalb jedes Schnipsels befindet, ist nicht wichtig. Aber manchmal gibt es einzelne Textschnipsel, 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 Problem bereits mit Regex in Perl gelöst, bis ich feststellte, dass ich nur die Fälle abgedeckt habe, in denen am Anfang, in der Mitte oder am Ende des Textes ein einzelner unpassender Text steht, aber nicht, wenn zwei unpassende Texte zusammenkommen. (wie die Y und Z Schnipsel oben).

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

Bearbeiten:

Das Problem besteht darin sicherzustellen, dass alles von eckigen Klammern umgeben sein sollte. Eckige Klammern sind niemals rekursiv. Wenn ein Satz mit eckigen Klammern umgeben wird, hängt der Wert des p-Werts vom "Label"-Wert ab. Wenn zum Beispiel ein unpassender Textschnipsel ist

li/IN

dann sollte es sich in folgendes verwandeln:

[PP li/IN]

Ich denke, es ist eine Mischung, aber der einzige Weg, den ich kenne, um das größere Problem zu lösen, an dem ich arbeite, besteht darin, alle in eckige Klammern umschlossenen Sätze zu wandeln, um die Bearbeitung zu erleichtern. Ich habe es also geschafft, wenn ein nicht in eckige Klammern eingeschlossener Satz am Anfang, in der Mitte und am Ende vorkommt, aber nicht, wenn zwei oder mehr zusammenkommen.

Im Grunde habe ich für jede Position (Anfang, Mitte und Ende) einen anderen Regex verwendet. Derjenige, der einen nicht in eckige Klammern eingeschlossenen Satz in der Mitte erfasst, sieht so aus:

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

Also, was ich mache, ist einfach festzustellen, dass, wenn ein ] vor und nach dem text/label-Muster steht, dieser keinen Klammern hat. Ich mache etwas Ähnliches auch für die anderen. Aber ich denke, das ist unglaublich unspezifisch. Mein Regex ist nicht optimal!

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(/(?

``

Dies ergibt

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

Ich habe angenommen, dass PP so gemeint war, wie ich es hier verwendet habe, andernfalls müsste die map etwas komplizierter werden.

EDIT

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

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

als Beispielschnur verwenden, lautet die Ausgabe wie folgt:

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

Nur eine Sache zu beachten: Das mit split verwendete Regex funktioniert nicht für pn mit n > 9. Falls Sie solche Fälle haben, suchen Sie am besten nach einer Alternative, da Variable Length Lookbehinds nicht implementiert wurden (zumindest in meiner Version von Perl (5.10.1)).

EDIT 2

Als Antwort auf Ihren zweiten Kommentar hier eine modifizierte Version des Skripts. Sie werden feststellen, dass ich auch etwas zur Beispielschnur hinzugefügt habe, um zu demonstrieren, dass es nun auch funktioniert, wenn kein pn innerhalb der [...] steht.

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

Die Ausgabe lautet:

[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 gesehen, dass Sie bereits die benötigte 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 geteilt, aber Sie sollten das g für den globalen Ersatz verwenden. Andernfalls ersetzt der Perl-Regularausdruck nur das erste Vorkommen

my $teststring = "hallo welt";

$teststring =~ s/o/X/;

wird zu hallX welt. aber

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

wird zu hallX wXrld und alle Übereinstimmungen werden beachtet.

Ich denke, Ihr Problem ist so etwas wie

my $teststring = ' A B C ';

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

ergibt [A] B [C]. Es wird nicht B bearbeitet und der Grund ist, dass im Rahmen der Übereinstimmung mit A die Regex-Maschinerie auch den Platz nach A verbraucht hat. Und im verbleibenden String gibt es keinen Platz vor B, daher stimmt es nicht überein.

Aber wenn Sie eine nicht gierige Übereinstimmung durchführen, wie zum Beispiel

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

ergibt es [A] [B] [C]

2voto

FailedDev Punkte 25987

Eigentlich können Sie dies nur mit Regex lösen:

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

Ausgabe:

[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