2009-09-18 263 views
12

考慮以下字符串:如何從Perl中的字符串中提取子字符串?

1)方案編號:ABC-456-hu5t10高優先級)*****

2)方案編號:FRT-78F-hj542w平衡

3)方案編號:23F-f974-nm54w超級式運行)*****

等以上述格式 - 粗體部分是跨字符串變化。

==>想象一下,我有很多字符串的格式如上所示。 我想從上述每個字符串中挑選3個子字符串(如下面的BOLD所示)。

  • 含有字母數字值(在例如高於它的「ABC-456-hu5t10」)包含單詞(在例如高於它的「高優先級」)
  • 含有第三子
  • 第二子第一子串* (IF *存在於字符串的末尾ELSE離開它)

如何選擇這些子3從上面所示的每個字符串?我知道它可以在Perl中使用正則表達式來完成......你能幫忙嗎?

+0

可以在括號中的字符串本身包含嵌套的括號? – 2009-09-18 12:02:21

回答

29

你可以做這樣的事情:

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

關鍵的是正則表達式:

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

如下打破了。

固定字符串 「計劃ID:」:

Scheme ID: 

後跟一個或多個字符A-Z,0-9或 - 。我們用括號來捕捉它爲$ 1:

([a-z0-9-]+) 

後跟一個或多個空格字符:

\s+ 

接着是左括號(我們逃),其次是任意數量的AREN字符不是右括號,然後是右括號(已轉義)。我們使用轉義括號來捕捉詞爲$ 2:

\(([^)]+)\) 

其次是一些空格的也許*,捕捉爲$ 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

\(([^)])\)會比\((。*?)\)好,因爲它保證在第一個位置停止。非貪婪的量詞可能導致嚴重的回溯,這會殺死性能。 (不可否認,在這種情況下,不可否認,但在不需要時避免它們仍然是一個培養良好習慣的習慣。)否定的角色階級也更清晰地表達了您的意圖 - 您正在尋找「 )字符「,而不是」任何字符的最小數字,然後是a「,這使得表達式成爲一個整體匹配」。 – 2009-09-19 10:19:04

3

你可以使用正則表達式,如下列:

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

因此,例如:

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

打印

abc-456-hu5t10 
High priority 
* 
1

很久沒有的Perl

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

串1:

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

字符串2:

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

的琴絃3:

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

好了,一個襯墊位置:

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

擴展爲一個簡單的腳本,以更好地解釋事情:

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

雖然如果它不是格式化的東西,我會實現一個主循環來處理文件並充實腳本的主體,而不是依賴命令行開關進行循環。

1

這只是需要一個小的變化,以我的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; 
相關問題