В Perl, как я могу получить соответствующую подстроку из регулярного выражения? - PullRequest
10 голосов
/ 15 июля 2009

Моя программа читает исходный код других программ и собирает информацию об используемых 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 и, вероятно, допускаю ошибку в базовой концепции. (позиция имени таблицы в строке не исправлена. Другая проблема - множественное вхождение, т.е. [... SELECT * FROM AADTTAB, BBDTTAB, ...]). Как я могу получить эту подстроку?

Ответы [ 6 ]

20 голосов
/ 15 июля 2009

Используйте группировку с круглыми скобками и сохраните первую группу.

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: v5.10.0 для MSWin32-x86-multi-thread

17 голосов
/ 15 июля 2009

Я предпочитаю это:

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

Это

  1. сканирует $line и захватывает текст, соответствующий шаблону
  2. возвращает «все» записи (1) в «список» на другой стороне.

Этот контекст псевдо-списка - это то, как мы ловим первый элемент в списке. Это делается так же, как параметры, передаваемые в подпрограмму.

my ( $first, $second, @rest ) = @_;


my ( $first_capture, $second_capture, @others ) = $feldman =~ /$some_pattern/;

ПРИМЕЧАНИЕ: : Тем не менее, ваше регулярное выражение предполагает слишком много текста, чтобы быть полезным в более чем нескольких ситуациях. Не захватывать имена таблиц, у которых нет dt, как в позициях 3 и 4 из 7? Это достаточно для 1) быстрого и грязного, 2) если у вас все в порядке с ограниченной применимостью.

8 голосов
/ 15 июля 2009

Было бы лучше сопоставить шаблон, если он следует 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 .

7 голосов
/ 15 июля 2009

Паренс позволит вам взять часть регулярного выражения в специальные переменные: $ 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";
}
3 голосов
/ 15 июля 2009

Используйте группу захвата:

$line =~ /(\S{2}DT\S{3})/i;
my $substr = $1;
0 голосов
/ 15 июля 2009

$& содержит строку, сопоставленную с последним совпадением шаблона.

Пример:

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

Так что вы могли бы сделать что-то вроде

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

ВНИМАНИЕ:

Если вы используете $& в своем коде, это замедлит все совпадения с шаблоном.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...