在Perl中,如何从正则表达式中获取匹配的子字符串?

时间:2009-07-15 15:13:52

标签: regex perl

我的程序读取其他程序的源代码并收集有关使用过的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的位置不固定。另一个问题是多次出现,即[... SELECT * FROM AADTTAB,BBDTTAB,...])。我怎样才能获得那个子串呢?

6 个答案:

答案 0 :(得分:20)

使用带括号的分组并存储第一组。

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

答案 1 :(得分:17)

我更喜欢这个:

my ( $table_name ) = $line =~ m/(\S{2}DT\S{3})/i;

这个

  1. 扫描$line并捕获与模式
  2. 对应的文本
  3. 将捕获(1)的“全部”返回到另一侧的“列表”。
  4. 这个伪列表上下文是我们如何捕获列表中的第一个项目。它的传递方式与传递给子程序的参数完全相同。

    my ( $first, $second, @rest ) = @_;
    
    
    my ( $first_capture, $second_capture, @others ) = $feldman =~ /$some_pattern/;
    

    注意::那就是说,你的正则表达式假设文本在多种情况下都有用。 没有捕获任何没有dt的表名,就像在7和4中的位置一样?它足够好1)快速和肮脏,2)如果你的适用性有限。

答案 2 :(得分:8)

如果模式遵循FROM,那么匹配模式会更好。我假设表名只包含ASCII字母。在这种情况下,最好说出你想要的。有了这两个注释,请注意在列表上下文中成功捕获正则表达式匹配会返回匹配的子字符串。

#!/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

答案 3 :(得分:7)

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";
}

答案 4 :(得分:3)

使用捕获组:

$line =~ /(\S{2}DT\S{3})/i;
my $substr = $1;

答案 5 :(得分:-1)

$&包含与上一次模式匹配匹配的字符串。

示例:

$str = "abcdefghijkl";
$str =~ m/cdefg/;
print $&;
# Output: "cdefg"

所以你可以做类似

的事情
if($line =~m/ \S{2}DT\S{3}/i) {
    print $&."\n";
}

警告:

如果您在代码中使用$&,则会减慢所有模式匹配。