2009-09-18 4 views
12

Considérez les chaînes suivantes:Comment puis-je extraire des sous-chaînes d'une chaîne en Perl?

1) Schéma ID: abc-456-hu5t10 (haute priorité) *****

2) ID Schéma: FRT-78f-hj542w ( équilibré)

3) schéma ID: 23f-f974-nm54w (super-formule initiée) *****

et ainsi de suite dans le format ci-dessus - les parties en gras sont des changements à travers les chaînes. Imaginez que j'ai beaucoup de chaînes de format Montré au-dessus de ==> Je veux choisir 3 sous-chaînes (comme indiqué en gras ci-dessous) de chacune des chaînes ci-dessus.

  • 1er sous-chaîne contenant la valeur alphanumérique (par exemple, dans au-dessus est « abc-456-hu5t10 »)
  • 2ème sous-chaîne contenant le mot (par exemple, dans au-dessus est « haute priorité »)
  • 3ème sous-chaîne contenant * (* IF est présent à la fin de la chaîne ELSE laisser)

Comment puis-je récupérer ces 3 sous-chaînes de chaque chaîne ci-dessus? Je sais que cela peut être fait en utilisant des expressions régulières en Perl ... Pouvez-vous m'aider?

+0

peut la chaîne entre parenthèses entre parenthèses imbriquées se contiennent? –

Répondre

29

Vous pouvez faire quelque chose comme ceci:

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"; 
} 

L'essentiel est l'expression régulière:

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

qui se décompose comme suit.

fixe String "ID Système":

Scheme ID: 

suivi d'un ou plusieurs des caractères A-Z, 0-9 ou -. Nous utilisons les supports pour capturer $ 1:

([a-z0-9-]+) 

Suivi par un ou plusieurs espaces:

\s+ 

Suivi par un support d'ouverture (qui nous échappons) suivi d'un nombre de caractères qui aren 't une parenthèse fermante, puis une parenthèse fermante (échappée). Nous utilisons les supports unescaped pour capturer les mots que 2 $:

\(([^)]+)\) 

Suivi par quelques espaces tout peut-être un *, capturés 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

\ (([^)]) \) serait mieux que \ ((. *?) \), Car il est garanti d'arrêter au premier). Les quantificateurs non gourmands peuvent provoquer un backtracking lourd, ce qui tue les performances. (Peu probable dans ce cas, certes, mais les éviter quand ils ne sont pas nécessaires est toujours une bonne habitude à cultiver.) La classe des personnages niés est aussi une déclaration plus claire de votre intention - vous cherchez "un nombre quelconque de non) ", pas" le plus petit nombre de tous les caractères, suivi de a), ce qui rend l'expression dans son ensemble ". –

3

Vous pouvez utiliser une expression régulière, comme les suivantes:

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

Ainsi, par exemple:

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

impressions

abc-456-hu5t10 
High priority 
* 
1

Long time no Perl

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

Chaîne 1:

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

Chaîne 2:

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

Chaîne 3:

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

Eh bien, une seule ligne ici:

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

Élargi à un script simple pour expliquer les choses un peu mieux:

#!/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) ... }  

Bien que si ce n'était autre chose que le formatage, j'implémenterais une boucle principale pour gérer les fichiers et étoffer le corps du script plutôt que de compter sur les commutateurs de ligne de commande pour la boucle.

1

Cela nécessite juste un petit changement à mes 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; 
Questions connexes