0

我正在尝试从字符串中提取 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
4

5 回答 5

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;

输出:

---这个,可以是 ['a'] s(@m)pl3 file name_with any characters---
---1923---
---720p---
---avi---
于 2012-05-01T12:39:03.963 回答
3

不管是否 Perl,有时正则表达式的问题在于它的贪心。假设我想捕获某人的名字,字符串如下所示:

Bob Baker

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

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

这适用于Bob Baker,但不适用于Bob Barry Baker。问题是我的正则表达式是贪婪的并且会选择所有字符直到最后一个空格,所以我最终不会Bob使用Bob Baker. 解决此问题的一种常用方法是指定除您不想要的字符之外的所有字符:

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

在这种情况下,我指定了任何包括空格的字符集。这将同时改变Bob BakerBob Rudolph Baker为 just Bob

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

$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

而且,此时,您不妨遵循 Damian Conway关于 Perl 正则表达式的最佳实践建议。

$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 所示,使用非贪婪正则表达式限定符能够解析a b c d e [1234] (1080p).mov,而使用不包含空格子表达式的所有内容则不能。这就是为什么我说它们不一样。

于 2012-05-01T16:21:21.163 回答
1

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

没测试过,写的更好:)

于 2012-05-01T12:45:21.887 回答
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}
于 2012-05-01T12:45:22.480 回答
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现在只包含三个字符串。

于 2012-05-01T15:29:04.877 回答