我正在尝试从字符串中提取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
答案 0 :(得分:3)
以下是一个考虑您的示例字符串的更新示例:
#!/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 Baker
和Bob 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
现在只包含三个字符串。