Regex Perl正则表达式-捕获所有字符,直到生成一个模式

Regex Perl正则表达式-捕获所有字符,直到生成一个模式,regex,perl,Regex,Perl,我试图从一个字符串中提取4个信息块。字符串是包含扩展名的文件名。在到达第二个组之前的空格之前,第一个组可以包含任何有效字符。第二组数据是一组方括号内包含的4个数字。该组由第一组以空格分隔。第三组可以是3或4个数字,后跟字母“p”。此组还与上一组之间用空格分隔。最后一组只是文件扩展名 下面是一个例子: This, could be ['a'] s(@m)pl3 file name_with any characters [1923] (720p).avi 然后需要将其解析为: $1 = This

我试图从一个字符串中提取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
另见

下面是一个考虑到示例字符串的更新示例:

#!/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--- ---这可能是['a']s(@m)pl3文件名_,包含任何字符--- ---1923--- ---720便士---
---avi--我会像这样编写正则表达式
(.*)(\[\d{4}\])(\(\d+p\)\。(.*)


还没有测试过,可以写得更好:)

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

(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}

看起来您正在尝试解析文件名。如果Sinan猜对了,它看起来像:

$x = 'a b c d e [1234] (1080p).mov'
现在,您可以编写一个正则表达式来解析它,但是对于不同的字符和复杂的正则表达式,维护起来可能会很痛苦,而且很容易中断。那么为什么不让它更容易使用呢

您也可以在单个空格上拆分
/
,但如果您在任何位置都有多个空格,则可能会出现多个空字段。而且它不会删除新行

当然,这完全取决于你想捕获哪些字段,但既然你没有提到这一点,我也帮不了你。请注意,以后也可以解析数组:

my @nums  = grep /\d/, @fields;       # anything with numbers
my ($tag) = grep /\[\d+\]/, @fields;  # catch first [1234] type field
关键是现在正则表达式更容易编写和维护

如果您依赖于从字符串末尾向后进行匹配,则可以将该函数与
split
结合使用,例如:

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

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

无论是否使用Perl,有时正则表达式的问题在于其贪婪性。假设我想捕获某人的名字,字符串如下所示:

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

sed 's/^\(.*)\ .*$/\1/'
这对鲍勃·贝克有效,但对鲍勃·巴里·贝克无效。问题是,我的正则表达式是贪婪的,会选择所有字符直到最后一个空格,因此我最终不会选择
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
此时,您最好遵循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所示,使用非贪婪正则表达式限定符能够解析
abcde[1234](1080p).mov
,而使用不包含空格子表达式的所有内容则不能。这就是为什么我说它们不一样。

不是“(任何一组字符)”简单的(.*?@MarcoS是的,但这也会匹配空格和制表符,它们可能不是文件名的一部分。我不知何故将其理解为任何非空格字符,但我可能错了。@Sylverdrag:我们可以假定吗?空格和制表符在文件名中都是完全合法的。这非常有用。我可以用它来提取我需要的东西。谢谢。我刚刚意识到我原来发布的内容有多不清楚。我还注意到我以前的假设有一个错误。为了清晰起见,我继续并完全重写了它。希望这能有所帮助,抱歉之前的混乱。抱歉,这是我在编辑评论时的疏忽。那些标签被添加了,我忘了删除关闭标签。谢谢!这正是我需要它做的,正如我最初的回答所指定的那样。事实上,在我意识到我不需要方括号和圆括号之后,我把它编辑成:(.*)[(\d{4})]((\d+p))\.(.*)。你对事情的解释非常有用。我同意将评论放在一边可以更容易地解释正在发生的事情。我注意到您在最后一个代码块的“#Period”行缺少一个右括号。我也无法通过使用“$string=~/”使其工作,但在将其更改为“$string=qr{”后,它工作了。
$string=~/../
假设您正在分析的字符串是
$string
我的$regex=qr(…)
将正则表达式保存在
regex
中,以便以后可以使用
$string=~/$regex/;
。我修复了缺少的括号。手动复制内容时会出现类似错误。
$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;