如何从Perl中的字符串中提取子字符串?

时间:2009-09-18 11:38:38

标签: regex perl string

考虑以下字符串:

1)方案ID: abc-456-hu5t10 高优先级)*****

2)方案ID: frt-78f-hj542w 平衡

3)方案ID: 23f-f974-nm54w 超级公式)*****

以上述格式

等等 - 粗体部分是字符串的变化。

==> 想象一下,我上面有许多格式的字符串。      我想从上面的每个字符串中选择3个子串(如下面的BOLD所示)。

  • 包含字母数字值的第一个子字符串(例如在其上面的“abc-456-hu5t10”)
  • 包含单词的第二个子字符串(例如在其上方的“高优先级”)
  • 包含*(IF *的第3个子字符串出现在字符串ELSE的末尾,离开它)

如何从上面显示的每个字符串中选择这3个子字符串?我知道可以使用Perl中的正则表达式来完成...你能帮忙解决这个问题吗?

7 个答案:

答案 0 :(得分:30)

你可以这样做:

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*(\*)?

分解如下。

固定字符串“Scheme ID:”:

Scheme ID: 

后跟一个或多个字符a-z,0-9或 - 。我们使用括号将其捕获为$ 1:

([a-z0-9-]+)

后跟一个或多个空格字符:

\s+

后面是一个左括号(我们将其转义),后跟任意数量的不是近括号的字符,然后是一个右括号(转义)。我们使用未转义的括号将单词捕获为$ 2:

\(([^)]+)\)

随后是一些空格,可能是*,被捕获为$ 3:

\s*(\*)?

答案 1 :(得分:3)

(\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 *

答案 2 :(得分: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
*

答案 3 :(得分:2)

嗯,这里有一个班轮:

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) ... }     

虽然如果它不是格式化,我会实现一个主循环来处理文件并充实脚本的主体,而不是依赖命令行开关进行循环。

答案 4 :(得分:1)

很久没有Perl

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

答案 5 :(得分: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;

答案 6 :(得分:0)

字符串1:

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

字符串2:

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

字符串3:

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