提问人:kato sheen 提问时间:7/15/2009 最后编辑:Donatkato sheen 更新时间:8/31/2023 访问量:67626
在Perl中,如何从正则表达式中获取匹配的子字符串?
In Perl, how can I get the matched substring from a regex?
问:
我的程序读取其他程序的源代码并收集有关使用的 SQL 查询的信息。我在获取子字符串时遇到问题。
...
$line = <FILE_IN>;
until( ($line =~m/$values_string/i && $line !~m/$rem_string/i) || eof )
{
if($line =~m/ \S{2}DT\S{3}/i)
{
# here I wish to get (only) substring that match to pattern \S{2}DT\S{3}
# (7 letter table name) and display it.
$line =~/\S{2}DT\S{3}/i;
print $line."\n";
...
结果,print 打印整行,而不是我期望的子字符串。我尝试了不同的方法,但我很少使用Perl,并且可能会犯基本概念错误。( tablename 在行中的位置不固定。另一个问题是多次发生,即[...选择 * 从 AADTTAB、BBDTTAB 等]).如何获取该子字符串?
答:
如果遵循,最好匹配该模式。我假设表名仅由 ASCII 字母组成。在这种情况下,最好说出你想要的。撇开这两个注释,请注意,在列表上下文中成功捕获正则表达式匹配将返回匹配的子字符串。FROM
#!/usr/bin/perl
use strict;
use warnings;
my $s = 'select * from aadttab, bbdttab';
if ( my ($table) = $s =~ /FROM ([A-Z]{2}DT[A-Z]{3})/i ) {
print $table, "\n";
}
__END__
输出:
C:\Temp> s
aadttab
根据系统上的版本,您可以使用命名的捕获组,这可能会使整个内容更易于阅读:perl
if ( $s =~ /FROM (?<table>[A-Z]{2}DT[A-Z]{3})/i ) {
print $+{table}, "\n";
}
请参见 perldoc perlre。
使用带括号的分组并存储第一个组。
if( $line =~ /(\S{2}DT\S{3})/i )
{
my $substring = $1;
}
上面的代码修复了拉出第一个表名的直接问题。但是,该问题还询问了如何提取所有表名。所以:
# FROM\s+ match FROM followed by one or more spaces
# (.+?) match (non-greedy) and capture any character until...
# (?:x|y) match x OR y - next 2 matches
# [^,]\s+[^,] match non-comma, 1 or more spaces, and non-comma
# \s*; match 0 or more spaces followed by a semi colon
if( $line =~ /FROM\s+(.+?)(?:[^,]\s+[^,]|\s*;)/i )
{
# $1 will be table1, table2, table3
my @tables = split(/\s*,\s*/, $1);
# delim is a space/comma
foreach(@tables)
{
# $_ = table name
print $_ . "\n";
}
}
结果:
如果 $line = “SELECT * FROM AADTTAB, BBDTTAB;”
输出:
AADTTAB
BBDTTAB
如果 $line = “SELECT * FROM AADTTAB;”
输出:
AADTTAB
Perl 版本:为 MSWin32-x86-multi-thread 构建的 v5.10.0
使用捕获组:
my $substr;
if( $line =~ /(\S{2}DT\S{3})/i ) {
$substr = $1;
}
评论
Parens 将允许您将正则表达式的一部分抓取到特殊变量中:$1、$2、$3...... 所以:
$line = ' abc andtabl 1234';
if($line =~m/ (\S{2}DT\S{3})/i) {
# here I wish to get (only) substring that match to pattern \S{2}DT\S{3}
# (7 letter table name) and display it.
print $1."\n";
}
$&
包含与最后一个模式匹配匹配的字符串。
例:
$str = "abcdefghijkl";
$str =~ m/cdefg/;
print $&;
# Output: "cdefg"
所以你可以做类似的事情
if($line =~m/ \S{2}DT\S{3}/i) {
print $&."\n";
}
警告:
如果在代码中使用,它将减慢所有模式匹配的速度。$&
评论
$&
$&
我更喜欢这个:
my ( $table_name ) = $line =~ m/(\S{2}DT\S{3})/i;
这
- 扫描并捕获与图案对应的文本
$line
- 将捕获的“所有”(1) 返回到另一端的“列表”。
这个伪列表上下文是我们捕获列表中第一项的方式。它的完成方式与传递给子例程的参数相同。
my ( $first, $second, @rest ) = @_;
my ( $first_capture, $second_capture, @others ) = $feldman =~ /$some_pattern/;
注意::也就是说,您的正则表达式对文本的假设太多,在少数情况下没有用。没有捕获任何没有 dt 的表名,如位置 3 和 4 中的 7?它足以满足 1) 快速和肮脏,2) 如果你对有限的适用性感到满意。
评论
my $foo = @bar;
my ($foo) = @bar;
当人们最初回答这个问题时,关于使用捕获的建议可能是一种方法。从那时起,Perl 就一直在前进,使用可能是现在最好的答案。$&
不使用捕获的一个重要原因:它会取消模式中所有其他捕获的编号。在这种情况下,您可以使用标记的捕获,例如 ,并查看它们或它们,这样您就没有数字了。(?<name>\w+)
%-
%+
另一个答案提到了 ,这是字符串中与模式匹配的部分。该答案还指出,它减慢了整个程序的速度,因为perl现在需要跟踪每个正则表达式的此信息,以防万一你将其用于该模式。$&
然而,Perl v5.20 开始在许多地方使用写时复制,这个问题变得毫无意义。Perl v5.18 也做了一些更改,因此它只跟踪您实际使用的特殊每场比赛变量,而不是所有三个变量 (, , )。$&
$`
$&
$'
以前,Perl v5.10 已经添加了开关,以启用一组并行的每场比赛变量,这些变量没有这种性能损失。这些变量只有长名称:/p
use v5.10;
if( $string =~ m/.../p ) {
say <<"HERE";
Before match: ${^PREMATCH}
Matched: ${^MATCH}
After match: ${^POSTMATCH}
HERE
}
并且,添加了 v5.26,因此您可以在不知道有多少捕获的情况下获得所有捕获的列表。但是,与其让第一项(索引 0)等价于 ,不如让一切都是一次性的。@{^CAPTURE}
$&
$1
评论