Regex Perl正则表达式-捕获所有字符,直到生成一个模式
我试图从一个字符串中提取4个信息块。字符串是包含扩展名的文件名。在到达第二个组之前的空格之前,第一个组可以包含任何有效字符。第二组数据是一组方括号内包含的4个数字。该组由第一组以空格分隔。第三组可以是3或4个数字,后跟字母“p”。此组还与上一组之间用空格分隔。最后一组只是文件扩展名 下面是一个例子: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
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;