2009-09-18 8 views
12

Betrachten Sie die folgenden Zeichenkette:Wie kann ich Teilstrings aus einer Zeichenkette in Perl extrahieren?

1) Schema ID: abc-456-hu5t10 (Hohe Priorität) *****

2) Schema ID: FRT-78f-hj542w (Balanced)

3) Schema ID: 23f-f974-nm54w (Super-Formel run) *****

und so weiter in dem obigen Format - die fett gedruckten Teile sind Änderungen über die Strings.

==>Stellen Sie sich vor, ich habe viele Zeichenketten Format oben gezeigt. Ich möchte 3 Teilstrings (wie unten in BOLD gezeigt) aus jeder der oben genannten Strings auswählen.

  • 1. Teilzeichenfolge den alphanumerischen Wert enthält, die das Wort enthalten
  • zweite Teilkette (in zB über ihm „abc-456-hu5t10“ ist), die
  • dritte Teilkette (in zB darüber „Hohe Priorität“ ist) * (IF * vorhanden ist, am Ende des Strings es ELSE zu verlassen)

Wie hole ich diesen 3 Teil von jeder Saite oben gezeigt? Ich weiß, dass es mit regulären Ausdrücken in Perl gemacht werden kann ... Können Sie dabei helfen?

+0

Kann die Zeichenfolge in Klammern selbst verschachtelt Klammern enthalten? –

Antwort

29

Sie so etwas tun könnte:

my $data = <<END; 
1) Scheme ID: abc-456-hu5t10 (High priority) * 
2) Scheme ID: frt-78f-hj542w (Balanced) 
3) Scheme ID: 23f-f974-nm54w (super formula run) * 
END 

foreach (split(/\n/,$data)) { 
    $_ =~ /Scheme ID: ([a-z0-9-]+)\s+\(([^)]+)\)\s*(\*)?/ || next; 
    my ($id,$word,$star) = ($1,$2,$3); 
    print "$id $word $star\n"; 
} 

Das Wichtigste ist, die Regulärer Ausdruck:

Scheme ID: ([a-z0-9-]+)\s+\(([^)]+)\)\s*(\*)? 

, die wie folgt aufbricht.

Die feste String "ID Schema:":

Scheme ID: 

gefolgt von einer oder mehreren der Zeichen a-z, 0-9 oder -. Wir verwenden die Klammern es zu erfassen, wie $ 1:

([a-z0-9-]+) 

Gefolgt von einem oder mehreren Leerzeichen:

\s+ 

Gefolgt von einer Öffnung Klammer durch eine beliebige Anzahl von Zeichen folgen (was wir entkommen), die aren eine enge Klammer und dann eine schließende Klammer (Escape). Wir verwenden unescaped Klammern die Worte als $ 2 zu erfassen:

\(([^)]+)\) 

Gefolgt von einigen Räumen jeder vielleicht ein *, gefangen wie $ 3:

\s*(\*)? 
2
(\S*)\s*\((.*?)\)\s*(\*?) 


(\S*) picks up anything which is NOT whitespace 
\s*  0 or more whitespace characters 
\(  a literal open parenthesis 
(.*?) anything, non-greedy so stops on first occurrence of... 
\)  a literal close parenthesis 
\s*  0 or more whitespace characters 
(\*?) 0 or 1 occurances of literal * 
+0

\ (([^)]) \) wäre besser als \ ((. *?) \), Da es garantiert bei der ersten stoppt. Nicht-gierige Quantifizierer können ein starkes Zurückverfolgen verursachen, wodurch die Leistung beeinträchtigt wird. (In diesem Fall ist es zugegebenermaßen unwahrscheinlich. Aber sie zu meiden, wenn sie nicht gebraucht werden, ist immer noch eine gute Angewohnheit.) Die negierte Charakterklasse ist auch eine klarere Aussage deiner Absicht - du suchst "eine beliebige Anzahl von) Zeichen ", nicht" die kleinste Zahl eines beliebigen Zeichens, gefolgt von a), wodurch der Ausdruck als Ganzes passt ". –

3

Sie können einen regulären Ausdruck wie den folgenden verwenden:

/([-a-z0-9]+)\s*\((.*?)\)\s*(\*)?/ 

So zum Beispiel:

$s = "abc-456-hu5t10 (High priority) *"; 
$s =~ /([-a-z0-9]+)\s*\((.*?)\)\s*(\*)?/; 
print "$1\n$2\n$3\n"; 

druckt

abc-456-hu5t10 
High priority 
* 
1

Lange nicht Perl

while(<STDIN>) { 
    next unless /:\s*(\S+)\s+\(([^\)]+)\)\s*(\*?)/; 
    print "|$1|$2|$3|\n"; 
} 
0

String 1:

$input =~ /'^\S+'/; 
$s1 = $&; 

String 2:

$input =~ /\(.*\)/; 
$s2 = $&; 

String 3:

$input =~ /\*?$/; 
$s3 = $&; 
1

Nun, ein Motto hier:

perl -lne 'm|Scheme ID:\s+(.*?)\s+\((.*?)\)\s?(\*)?|g&&print "$1:$2:$3"' file.txt 

Erweitert zu einem einfachen Skript, um die Dinge ein wenig besser zu erklären:

#!/usr/bin/perl -ln    

#-w : warnings     
#-l : print newline after every print        
#-n : apply script body to stdin or files listed at commandline, dont print $_   

use strict; #always do this.  

my $regex = qr{ # precompile regex         
    Scheme\ ID:  # to match beginning of line.      
    \s+    # 1 or more whitespace        
    (.*?)   # Non greedy match of all characters up to   
    \s+    # 1 or more whitespace        
    \(    # parenthesis literal        
    (.*?)   # non-greedy match to the next      
    \)    # closing literal parenthesis      
    \s*    # 0 or more whitespace (trailing * is optional)  
    (\*)?   # 0 or 1 literal *s         
}x; #x switch allows whitespace in regex to allow documentation. 

#values trapped in $1 $2 $3, so do whatever you need to:    
#Perl lets you use any characters as delimiters, i like pipes because      
#they reduce the amount of escaping when using file paths   
m|$regex| && print "$1 : $2 : $3"; 

#alternatively if(m|$regex|) {doOne($1); doTwo($2) ... }  

Wenn es jedoch etwas anderes als Formatierung wäre, würde ich eine Hauptschleife implementieren, um Dateien zu bearbeiten und den Rumpf des Skripts auszufüllen, anstatt mich auf die Befehlszeilenschalter für das Looping zu verlassen.

1

Dies erfordert nur eine kleine Änderung an meine last answer:

my ($guid, $scheme, $star) = $line =~ m{ 
    The [ ] Scheme [ ] GUID: [ ] 
    ([a-zA-Z0-9-]+)   #capture the guid 
    [ ] 
    \( (.+) \)    #capture the scheme 
    (?: 
     [ ] 
     ([*])    #capture the star 
    )?      #if it exists 
}x; 
Verwandte Themen