Perl Regex - 捕获所有字符,直到模式

时间:2012-05-01 12:33:51

标签: regex perl

我正在尝试从字符串中提取4个信息块。该字符串是包含扩展名的文件的名称。第一组可以包含任何有效字符,直到达到第二组之前的空格。第二组数据将包含在一组方括号内的4个数字。该组由第一组由空格分隔。第三组可以是3或4个数字,后跟字母“p”。此组也由前一组的空格分隔。最后一组只是文件扩展名。

以下是一个例子:

This, could be ['a'] s(@m)pl3 file name_with any characters [1923] (720p).avi

然后需要将其解析为:

$1 = This, could be ['a'] s(@m)pl3 file name_with any characters
$2 = 1923
$3 = 720p
$4 = avi

5 个答案:

答案 0 :(得分:3)

另见perldoc perlreref

以下是一个考虑您的示例字符串的更新示例:

#!/usr/bin/env perl

use strict; use warnings;

my $x = q{This, could be ['a'] s(@m)pl3 file name_with any characters [1923] (720p).avi};

my $pat = qr{
    \A
    (.+?)
    [ ]
    \[ ( [0-9]{4} ) \]
    [ ]
    \( ( [0-9]+ p ) \)
    [.]
    (.+)
    \z
}x;

print "---$_---\n" for $x =~ $pat;

输出:

---This, could be ['a'] s(@m)pl3 file name_with any characters---
---1923---
---720p---
---avi---

答案 1 :(得分:3)

无论是否Perl,有时正则表达式的问题是它的贪婪。假设我想捕获某人的名字,字符串看起来像这样:

Bob Baker

我可以使用这个正则表达式:

sed 's/^\(.*)\ .*$/\1/'

这适用于 Bob Baker ,但不适用于 Bob Barry Baker 。问题是我的正则表达式是贪婪的,并且会选择 last 空间之前的所有字符,因此我最终不会使用Bob而是使用Bob Baker。解决此问题的常用方法是为您不想要的字符指定之外的所有字符:

sed 's/^\([^ ]*)\ .*$/\1/'

在这种情况下,我指定任何一组字符,包括空格。这会将Bob BakerBob Rudolph Baker都更改为Bob

Perl有另一种指定非贪婪正则表达式的方法。在Perl中,您将?添加到您想要不贪婪的子表达式中。在上面的示例中,这两个都会将包含Bob Barry Baker的字符串更改为Bob

$string =~ s/^([^ ]+) .*$/$1/;
$string =~ s/^(.+?) .*$/$1/;

顺便说一句, 等同于

使用除空格正则表达式之外的所有东西,我可以这样做:

 $string =~ /^([^ ]+)( )(\[\d{4}\])( )(\(\d+p\))(\.)([^.]+)/

使用非贪婪的限定符:

$string =~ /^(.+?)( )(\[\d{4}\])( )(\(\d+p\))(\.)(.*)/

并且,使用x限定符,它允许您将相同的正则表达式放在多行上,这很好,因为您可以添加注释以帮助解释您正在做的事情:

$string =~ /
     ^(.+?)                   #Any set of characters (non-greedy)
     ([ ])                    #Space
     (\[\d{4}\])              #[1959]
     ([ ])                    #Space
     (\([0-9]+p\))            #(430p)
     [.]                      #Period
     ([^\.]+)                 #File Suffix (no period)
/x

而且,此时,您可能会关注Perian正则表达式中关于Damian Conway的 Best Practice 的建议。

$string =~ /
     \A                 #Start of Regular Expression Anchor
     ( .+? )            #Any set of characters (non-greedy)
     ( [ ] )            #Space
     ( \[ \d{4} \] )    #[1959]
     ( [ ] )            #Space
     ( \( [0-9] +p \) ) #(430p)
     ( [.] )            #Period
     ( [^\.]+ )         #File Suffix (no period)
     \Z                 #End of string anchor
/xm;

由于x忽略所有空格,我甚至可以在同一行的子组之间添加空格。在这种情况下,( .*+? )(.*+?)更清晰一点。 ( \( [0-9] +p \) )( \( [0-9]+p \) )甚至( \([0-9]+p\) )更容易理解取决于您。

而且,是的答案看起来非常像Sinan's

顺便说一下,正如Sinan所示,使用非贪婪的正则表达式限定符能够解析a b c d e [1234] (1080p).mov,同时使用不包含空格的所有内容子表达式“T。这就是我说他们不一样的原因。

答案 2 :(得分:1)

我会写这样的正则表达式(.*?) (\[\d{4}\]) (\(\d+p\))\.(.*)

Haven没有对它进行过测试,可以写得更好:)

答案 3 :(得分:0)

我不使用Perl,所以我的正则表达式可能需要一些调整,但是AFAIK:

(any set of characters) = \S*
(a space) = \s+
('[' + 4 numbers + ']') = \[[0-9]{4}
(a space) = \s+
('(' + an unknown number of numbers + 'p)') = \([0-9]+p\)
(a period) = \.
(file extension)  = .{2,5}

答案 4 :(得分:0)

这看起来像是在尝试解析文件名。如果思南猜对了,它看起来像是:

$x = 'a b c d e [1234] (1080p).mov'

现在,您可以编写一个正则表达式来解析它,但是对于不同的字符和复杂的正则表达式,维护并且易于破解可能会很痛苦。那么为什么不轻松使用split

my @fields = split ' ', $x; 

您也可以拆分单个空格/ /,但如果您在任何地方有多个空格,则会冒多个空字段的风险。并且它不会删除换行符。

这一切都取决于你想要捕捉的领域,当然,但由于你没有提到这一点,我无法帮助你。请注意,您之后也可以解析数组:

my @nums  = grep /\d/, @fields;       # anything with numbers
my ($tag) = grep /\[\d+\]/, @fields;  # catch first [1234] type field

关键是现在正则表达式更易于编写和维护。

如果您依赖于从字符串末尾开始匹配,可以将reverse函数与split结合使用,例如:

my $xrev   = reverse $x;
my @fields = split ' ', $xrev, 3; 

“3”是对创建的字段数量的限制,因此@fields现在只包含三个字符串。