Приложение Б

Программа поиска адресов электронной почты

Ниже приведена оптимизированная версия программы из раздела «Поиск адресов электронной почты» (стр. <$R[P#,R7-15]>). Текст программы вы можете найти по адресу http://www.piter.com/download.

# Вспомогательные переменные, позволяющие избавиться от лишних символов \.

$esc          = '\\\\';        $Period          = '\.';

$space        = '\040';        $tab         = '\t';

$OpenBR       = '\[';          $CloseBR     = '\]';

$OpenParen    = '\(';          $CloseParen  = '\)';

$NonASCII     = '\x80-\xff';   $ctrl        = '\000-\037';

$Crlist       = '\n\015'; # Примечание: должно быть \015.

 

# Элементы 19, 20, 21

$qtext = qq/[^$esc$NonASCII$CRlist"]/;                # внутри "..."

$dtext = qq/[^$esc$NonASCII$CRlist$OpenBR$CloseBR]/; # внутри [...]

$quoted_pair = qq< $esc [^$NonASCII] >; # экранированный символ

 

# Элементы 22 и 23 - комментарий.

# Реализовать общий случай при помощи регулярного выражения

# невозможно. В данном примере допускается один уровень вложенности.

$ctext     = qq< [^$esc$NonASCII$CRlist()] >;

 

# $Cnested совпадает с одним не-вложенным комментарием.

# При раскрутке нормальным элементом считается $ctext,

# а специальным - $quoted_pair.

$Cnested = qq<

    $OpenParen                              # (

        $ctext* #        норм*

        (?: $quoted_pair $ctext* )* #       (спец норм*)*

    $CloseParen #                    )

>;

 

# $comment допускает один уровень вложенности для круглых скобок.

# При раскрутке нормальным элементом считается $ctext,

# а специальным - ($quoted_pair|$Cnested)

$comment = qq<

    $OpenParen                             # (

        $ctext* #        норм*

        (?: #        (

           (?: $quoted_pair | $Cnested ) #         спец

           $ctext* #           норм*

        )* #             )*

    $CloseParen #              )

>;

# $X - необязательные пропуски/комментарии.

$X = qq<

    [$space$tab]*                 # Пропуски.

    (?: $comment [$space$tab]* )* # Если комментарий найден, возможны

                                  # присутствие дополнительных пропусков.

>;

 

# Элемент 10: atom

$atom_char = qq/[^($space)<>\@,;:".$esc$OpenBR$CloseBR$ctrl$NonASCII]/;

$atom = qq<

$atom_char+      # Некоторое количество символов атома...

(?!$atom_char) # ..за которыми не следует нечто,

                  # не являющееся частью атома

>;

 

# Элемент 11: раскрученная строка в кавычках.

$quoted_str = qq<

    "                                   # "

        $qtext * #       норм

        (?: $quoted_pair $qtext * )* #   ( спец норм* )*

    " #                    "

>;

 

# Элемент 7: word - атом или строка в кавычках

$word = qq<

    (?:

        $atom                  # Атом

        | #    or

           $quoted_str # Строка в кавычках

    )

>;

 

# Элемент 12: domain-ref - просто atom

$domain_ref  = $atom;

 

# Элемент 13: domain-literal - аналог строки в кавычках, но вместо

# "..." используется [...]

$domain_lit  = qq<

    $OpenBR                       # [

    (?: $dtext | $quoted_pair )* #      содержимое

    $CloseBR                      #               ]

>;

 

# Элемент 9: sub-domain - domain-ref или domain-literal

$sub_domain  = qq<

(?:

    $domain_ref

    |

    $domain_lit

    )

    $X # optional trailing comments

>;

 

# Элемент 6: domain - список субдоменов (subdomain), разделенных точками.

$domain = qq<

    $sub_domain

    (?:

           $Period $X $sub_domain

        )*

>;

 

# Элемент 8: route. Последовательность "@ $domain", разделенных запятыми,

# и завершающаяся двоеточием.

$route = qq<

    \@ $X $domain

    (?: , $X \@ $X $domain )* # Дополнительные домены

    :

    $X # Необязательные комментарии в конце

>;

 

# Элемент 6: local-part - последовательность $word, разделенных точками.

$local_part = qq<

    $word $X

    (?:

        $Period $X $word $X # Дополнительные слова

    )*

>;

 

# Элемент 2: addr-spec - local@domain

$addr_spec  = qq<

$local_part \@ $X $domain

>;

 

# Элемент 4: route-addr - <route? addr-spec>

$route_addr = qq[

    < $X                # <

        (?: $route )? #     optional route

        $addr_spec #        address spec

    > #              >

];

# Элемент 3: phrase       

$phrase_ctrl = '\000-\010\012-\037'; # Аналог ctrl, но без табуляции

 

# Аналог atom-char, но без пробела и с phrase_ctrl.

# Поскольку класс является инвертированным, он совпадает с теми же

# символами, что и atom-char, а также с пробелом и табуляцией

$phrase_char =

   qq/[^()<>\@,;:".$esc$OpenBR$CloseBR$NonASCII$phrase_ctrl]/;

 

# $word, $comment и $quoted_str не должны поглощать конечные экземпляры $X,

# поскольку мы это делаем вручную.

$phrase = qq<

    $word                         # Начальное слово

    $phrase_char * # "нормальные" атомы и/или пропуски

    (?:

        (?: $comment | $quoted_str ) # "специальный" комментарий

                                     # или строка в кавычках

        $phrase_char * # "нормальные" элементы

    )*

>;

 

## Элемент 1: mailbox - addr_spec или phrase/route_addr

$mailbox = qq<

    $X                         # Необязательный начальный комментарий

    (?:

           $addr_spec # address

           | #  или

           $phrase $route_addr # Имя и адрес

        )

>;

После удаления пропусков и комментариев командами

$mailbox =~ s/#.*//g;

$mailbox =~ s/\s+//g;

(это можно сделать, поскольку наши символьные классы заведомо не содержат пробелов и #) получается регулярное выражение, состоящее из 6598 байт. И все же оно работает быстрее 4724-байтового выражения, построенного в главе 7. Приведу оптимизированный вариант выражения на случай, если вам вдруг захочется ввести его вручную.

[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-

\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\

xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80

-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n

\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\

x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\

[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\

040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xf

f]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)

*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\0

00-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\

\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]

*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\0

15()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\01

5()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[

^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n

\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:"

.\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xf

f])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^

\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?

:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\

)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()

]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\

]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[

(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80

-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\

x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040

\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,

;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xf

f][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-

\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\

xff\n\015()]*)*\)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\x

ff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*)*<[\04

0\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff

\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\

n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(

?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\

xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>

@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x8

0-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?

:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015(

)]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()

]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^

\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\

015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".

\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff

])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\

\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:

\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)

[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()

]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t]*(?:\([^\\\x80-

\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x

80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\

t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".

\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xf

f])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^

\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\

\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n

\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xf

f][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(

?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]

\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\]

)[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80

-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-

\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015()

]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\

\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^

(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000

-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xf

f\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xf

f]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)

*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80

-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\

x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040

\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:"

.\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][

^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\

\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff

\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\

([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]

*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*

)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\04

0)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\

[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()

]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\

x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\

[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\

040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,

;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80

-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|

\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\)

)[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)

Развлекайтесь!