Опубликован: 18.06.2007 | Доступ: свободный | Студентов: 1354 / 35 | Оценка: 4.14 / 3.29 | Длительность: 12:44:00
ISBN: 978-5-94774-604-4
Лекция 6:

Предотвращение зацикливания при поиске и замене. Якорь \G, итеративный поиск с модификаторами g и gc

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >

6.3 Лексический анализ текста с помощью якоря \G и модификатора gc

С помощью конструкции /\G шаблон /gc можно делать лексический анализ текста, производя извлечение из него нужных фрагментов один за другим, без пропусков. Это учебный пример, который показывает принцип разбора:

my $text='123 234 345 456';
while ($text =~ /\d+/g) { print "Найдено число $&\npos=".pos($text)."\n" }

На печати увидим:

Найдено число 123
pos=3
Найдено число 234
pos=7
Найдено число 345
pos=11
Найдено число 456
pos=15

Отпечатаются позиции за каждым найденным числом.

Предположим, что мы делаем разбор текста на имена, которые состоят из латинских букв, чисел и символов перевода строк. Мы должны последовательно извлекать из текста эти объекты, называть и выводить их. Если встречается что-то, что не соответствует синтаксису ни одного объекта, то это будет называться unknown (неизвестно). Регулярное выражение будет начинаться с \G и представлять собой высокоуровневую конструкцию выбора:

$_=<<EOF;
123 abc
-234 def
\$\@ xyz
EOF
while (/\G
(?:(?>[\000-\011\013-\040]+)|           	 # все от \0 до пробела кроме \n
   (\b(?>[a-zA-Z]+)\b)(?{ print "Name: $^N\n" })|       # имя
   ((?>[+-]?)(?>\d+)\b)(?{ print "Number: $^N\n" })|   # число
   (\n)(?{ print "Newline:\n" })|                # \n
   ((?>\S+))(?{ print "Unknown: $^N\n" })   # все остальное
)/gx) {}

Чтобы отделить первую альтернативу выбора от \G, необходимо всю конструкцию выбора взять в скобки. На печать выйдет

Number: 123
Name: abc
Newline:
Number: -234
Name: def
Newline:
Unknown: $@
Name: xyz
Newline:

В этом примере обратите внимание на то, что ветка \S+ для unknown поставлена последней, иначе разбор получился бы некорректным.

Не обязательно все шаблоны хранить в одной большой конструкции выбора, можно делать разбор текста по нескольким регулярным выражениям, но тогда надо использовать модификатор gc. Вот упрощенный пример программы проверки кода HTML с проверкой правильности закрытия тега A:

#!/usr/bin/perl -w
use strict;
use bytes;
use locale;

$_=<<EOF;
<html>
<body>
&lt;слово1 
слово2 1234&gt;
<a href="http://www.intuit.ru">Это ссылка</a>
</body>
</html>
EOF

my $close_a=0;
while (!/\G\z/gc)
 { if (/\G<(\w+)[^>]*>\s*/gc)
    { print "Тег $^N открылся\n";
      if ($^N =~ /^a$/i)
       { print "Ошибка: вложенный тег A\n" if $close_a++;
       }
    }
    elsif (m!\G</(\w+)[^>]*>\s*!gc)
     { print "Тег $^N закрылся\n";
       if ($^N =~ /^a$/i)
        { print "Ошибка: нет парного тега A\n" if !$close_a--;
        }       
     }
     elsif (/\G(\w+)\s*/gc)
      { print "Найдено слово/число $^N\n";
      }
      elsif (/\G(&#?\w+;)\s*/gc)
       { print "Найдена подстановка $^N\n";
       }
       # Пропускаем все кроме тегов, слов и подстановок
       elsif (/\G[^<>&\w]+/gc)
        {
        }
        else
            # Нашли ошибку, сделаем сообщение об этом
         { my $offset=pos($_);
           my ($error)=$_ =~ /\G.{1,10}/gs;
           die "Непонятные символы\n$error\nв позиции $offset\n";
         }
 }
print "Имеется незакрытый тег A\n" if $close_a;

Эта программа напечатает следующее:

Тег html открылся
Тег body открылся
Найдена подстановка &lt;
Найдено слово/число слово1
Найдено слово/число слово2
Найдено слово/число 1234
Найдена подстановка &gt;
Тег a открылся
Найдено слово/число Это
Найдено слово/число ссылка
Тег a закрылся
Тег body закрылся
Тег html закрылся
< Лекция 5 || Лекция 6: 1234 || Лекция 7 >
Константин Бражников
Константин Бражников
Россия
Mike .
Mike .
Россия