Copyright: © 1999-2001 Andrey V. Sorokin, anso@mail.ru


Назначение

TRegExpr - это мощный инструмент для проверки ввода на соответствие шаблонам (в приложениях БД, web и т.д.), для поиска и замены подстрок, создания egrep-подобных утилит и т.д.

Вы можете легко проверить синтаксическую корректность e-mail адреса, извлечь номер телефона или почтовый индекс из неформализованного текста, находить необходимую информацию в  web-страницах - возможности ограничены только Вашим воображением. Обратите внимание, что правила (шаблоны) проверки и поиска можно изменять не перекомпилируя программу !

Эта библиотека портирована из библиотеки V8 Henry Spencer и позволяет работать с подмножеством Регулярных Выражений языка Перл.

TRegExpr написан на 100% object pascal (исходные тексты доступны бесплатно). Для использования TRegExpr не нужны никакие DLL ! Кроме того, оригинальные Си-исходники были доработаны некоторыми расширениями из Perl и "обернуты" в удобную объектную оболочку TRegExpr (с реализацией можно ознакомится в файле RegExpr.pas).

Для начала Вы можете ознакомится с рядом небольших примеров использования, если Вы не знакомы с регулярными выражениями, рекомендую просмотреть краткое описание синтаксиса (для изучения и экспериментов удобно использовать Демо-программу).

TRegExpr может работать с unicode-строками (см. как это делать).

История изменений описана в разделе что нового.

Не стесняйтесь присылать Ваши предложения и сообщения о найденных ошибках.

Автор с удовольствием примет помощь в переводе этого руководства на другие языки.


Лицензия

 Copyright (c) 1999-2001 by Andrey V. Sorokin <anso@mail.ru>

 Этот продукт предоставляется "как есть" без каких бы то ни было гарантий.

 Вы можете использовать этот продукт в любых Ваших разработках, включая коммерческие, распространять и корректировать его при условии выполнения нижеследующих ограничений :

 1. Должно быть оставлено упоминание автора оригинальной версии, Вы не должны утверждать что разработали продукт самостоятельно. Желательно также, наличие в конечном продукте упоминание о факте использования данной библиотеки (в документации или диалоге "О программе").

 2. Вы не имеете права распространять данный продукт за деньги, если он не интегрирован в Вашу разработку. Если продукт включен в Вашу разработку, то Вы не имеете права требовать деньги за предоставление исходных текстов.

---------------------------------------------------------------

     Legal issues for the original C sources:

---------------------------------------------------------------

 *  Copyright (c) 1986 by University of Toronto.

 *  Written by Henry Spencer.  Not derived from licensed software.

 *

 *  Permission is granted to anyone to use this software for any

 *  purpose on any computer system, and to redistribute it freely,

 *  subject to the following restrictions:

 *  1. The author is not responsible for the consequences of use of

 *      this software, no matter how awful, even if they arise

 *      from defects in it.

 *  2. The origin of this software must not be misrepresented, either

 *      by explicit claim or by omission.

 *  3. Altered versions must be plainly marked as such, and must not

 *      be misrepresented as being the original software.


Что нового

Расшифровка:

 (+) добавлено

 (-) устранена ошибка

 (^) доработки

v. 0.942 2001.02.12

•	(-) Ошибка "Range-check" в демо-программе (из-за ошибки в RegExprSubExpressions), Обнаружена Juergen Schroth

•	(^) RegExprSubExpressions - добавлены коды возврата для ошибки "незакрытая "["

•	(^) Небольшие исправления этой документации

 v. 0.941 2001.02.01

•	(^) Внимание! Изменилась обработка '\w', '\W'! Теперь они действительно работают с буквами, цифрами и '_' как и описано в документации (до этого я забыл включить цифры). Спасибо Vadim Alexandrov. Если необходимо восстановить то, что было в предыдущих версиях, просто уберите из RegExprWordChars символы '0123456789'.

•	(+) Реализована полностью соответствующая описанию на unicode.org работа в многострочном режиме (модификатор /m), включая как DOS-разделители строк (\r\n) так и Unix (\n), а также любые их комбинации - см LineSeparators, LinePairedSeparator

•	(^) Внимание! Изменилась обработка '.'! Теперь, если модификатор /s выключен, то '.' не совпадает с символами, определенными в LineSeparators и LinePairedSeparator (по умолчанию для не-UniCode это \r и \n)

•	(^) Внимание! Чтобы избежать ненужных перекомпиляций выражения, теперь присвоение выражения свойству Expression или изменение значений модификаторов не приводит к немедленной [пере]компиляции. Т.о. исключение вызванное ошибками в выражении будет возникать не при присвоении ошибочного выражения, а при обращении к методам Exec[Next], Substitute, Dump, и т.д

•	(+) Реализованы "нежадные" повторители (например '*?'),и модификатор /g. Помогали Matthew Winter и Filip Jirsak

•	(+) модификатор /x ("расширенный синтаксис" позволяющий наглядно форматировать выражения)

•	(+) Процедура Compile для [пере]компиляции регулярного выражения. Полезна для визуальных редакторов регулярных выражений (проверка корректности выражения).

•	(+) ЧАВО. Я слишком устал отвечать на одни и те же вопросы снова и снова :(

•	(^) демо-программа существенно доработана. Теперь это вполне можно назвать отладчиком регулярных выражений! Спасибо Jon Smith за его идеи.

•	(+) функция RegExprSubExpressions, полезна для визуальных редакторов регулярных выражений (пример использования см. в демо-программе)

•	(+) Модуль HyperLinkDecorator - реальный пример использования TRegExpr

•	(-) Ошибка Range checking в некоторых случаях (при включенном ComplexBraces)  Спасибо Juergen Schroth

•	(^) ComplexBraces теперь включен по умолчанию - я наконец понял что только так его можно реально оттестировать ;)

•	(+) Kit Eason прислал массу примеров для документации, что побудило меня полностью переписать описание синтаксиса.

•	(+) Метасимволы \A и \Z - они работают также как "^'' и "$'' соответственно, но их поведение не переключается модификатором /m.

 v. 0.938 2000.07.23

•	(^) При возникновении ошибок, компилятор теперь будет показывать строку в которой ошибка произошла, а не процедуру Error где всегда вызывается exception (Я не вполне уверен в стабильности (это может зависеть от настроек компилятора). Если будут глюки - уберите определение reRealExceptionAddr из regexpr.pas И НЕ ЗАБУДЬТЕ ПОЖАЛОВАТЬСЯ МНЕ !).

•	(^) Забыл обрабатывать BSUBEXP[CI] в FillFirstCharSet, соответственно возникала ошибка  'memory corruption' (но только если выражение допускало начало проверки с backreference, например: (a)*\1 (первого подвыражения может не быть и мы начинаем с обратной ссылки).

 v. 0.937 2000.06.12

•	(-) Очень неприятный баг (начиная с v.0.934, и почему никто раньше не заметил ?). При использовании '^' иногда не находились корректные строки.

    Обнаружил Matthias Fichtner

 v. 0.936 2000.04.22

•	(+) Back references (ссылки на уже совпавшие части подвыражения), например '<font size=(['"]?)(\d+)\1>'. Подробнее см. в описании синтаксиса.

•	(+) UniCode символы можно задать в шестнадцатеричном формате '\x{nnnn}'

 v. 0.935 2000.04.19 (by Yury Finkel)

•	(-) fInvertCase теперь можно изменять не только внутри объекта

•	(-) Были синтаксические 8-) ошибки в реализации режима UniCode

 v. 0.934 2000.04.17

•	(^) Новая реализация диапазонов. Очень быстрая - на одной команде процессора !

•	(^) Структура откомпилированных выражений теперь 32-разрядная. Это быстрее работает и теперь нет 64K-ограничения на откомпилированное выражение.

•	(^)'{m,n}' теперь используют 32разрядные аргументы (т.е. до 2147483646) - специально для Dmitry Veprintsev ;)

•	(^) Можно использовать метасимволы в диапазонах: [\n-\x0D] -> #10,#11,#12,#13; Изменен алгоритм парсинга диапазонов - теперь он максимально совместим с Perl: [\d-t] -> '0'..'9','-','t'; []-a] -> ']'..'a'

•	(-) Баг - \t и прочие работали только внутри диапазонов (исправил Yury Finkel)

•	(^) Существенно оптимизирован предварительный просмотр входных строк (см. FirstCharSet). В некоторых случаях скорость возрастает на два порядка.

•	(^) Многие другие фрагменты кода оптимизированы по скорости

•	(-) Нормально работает с национальными символами регистро-независимый режим (из-за ошибки в v.0.90 .. 0.926 он работал только для английских символов)

•	(^) Регистро-независимый режим реализован через InvertCase работающую с REChar а не строками, что ускорило регистро-независимые сравнения на один-два порядка.

•	(^) Обертки Match и ExecNext оптимизированы, добавлена IsProgrammOk (реализовал Ralf Junker)

•	(^) Число подвыражений по умолчанию (NSUBEXP) увеличено до 15). Код переработан таким образом что теперь эту константу легко можно изменять (раньше она была жестко "вшита" в код). Не советую увлекаться - большие значения замедлят работу и увеличат требования к стеку. Предложил Alexander V. Akimov.

•	(^+) Substitute доработана для поддержки NSUBEXP > 10 и существенно (!) ускорена, доработана проверка входных данных.  ВНИМАНИЕ! Обязательно ознакомьтесь с новым описанием Substitute - синтаксис ее шаблонов изменился !

•	(+) SpaceChars & WordChars property - позволяют задать символы, воспринимаемые как \s & \w. По умолчанию инициализируются из глобальных констант RegExprSpaceChars/WordChars

•	(+) \s и \w теперь можно включать в диапазоны

•	(-) Бесконечный цикл если верхняя граница диапазона=#$FF Обнаружен Andrey Kolegov

•	(+) Function QuoteRegExprMetaChars (см.описание)

•	(+) Поддержка UniCode. Не увлекайтесь - работает весьма медленно (точнее, раз в 5 быстрее чем TRegExpr v.0.926 ;) ). Как включить - описано в документации. Реализовал Yury Finkel

 v. 0.926 2000.02.26

•	(-) Несущественная ошибка еще H.Spencer-а - SPSTART устанавливается '?' и '*' вместо '*', '{m,n}' и '+'.

•	(-^) Теперь {m,n} работает как в Perl - ошибка компиляции происходит только если m > n или n > BracesMax (BracesMax = 255 в этой версии). В остальных случаях (нет m или нецифровые символы в m или n,  или нет '}') символ '{' будет воспринят как обычный а не метасимвол.  Таким образом, теперь обязательно указывать m (используйте {0,n} а не {,n}).  Внимание: {m,} будет воспринят как {m,BracesMax}.

•	(-^) CaseInsensitive (регистро-независимый) режим теперь распространяется и на диапазоны '(?i)[a]' == '[aA]'

•	(^) В TestRExp добавлен шаблон для римских чисел ;)

•	(+^) Бета-версия итераторов для подвыражений ((abc){1,2}|d){3} По умолчанию реализация отключена. Если Вы желаете поучаствовать в бета-тестировании - уберите  '.' из {.$DEFINE ComplexBraces} в файле regexpr.pas.

•	(-^) Убран метасимвол \b (чтобы не смущать любителей Perl где он является метасимволом границы слов а вовсе не BS как было реализовано у меня)

•	(+) Добавлен модификатор /s. Но я еще не уверен что это правильная реализация для Windows. Дело в том, что сейчас он работает как [^\n] для '.' если /s отключен. Но в windows принято разделять строки \n\r. Если у Вас есть предложения - обязательно сообщите !

•	(^) Очень жаль, но пришлось переименовать Modifiers в ModifierStr (ModifierS сейчас используется для прямого доступа к модификатору /s)

 v. 0.91 2000.02.02

•	(^) подправлена документация.

 v. 0.90 2000.02.01

(+) реализованы {min,max}. К сожалению, пока только для вырожденных случаев типа '\d{2,3}' или '[a-z1-9]{,7}', а вот варианты типа (abc){2,3} не будут работать.. Особых проблем с реализацией нет, у меня просто нет на это времени...

•	(+) работа без учета регистра + возможность управлять этим и другими (в будущем) модификаторами как глобально так и внутри выражения - подробнее см. в описании  Modifiers, ModifierR, ModifierI и (?ismxr-ismxr) . Для изменения значений по умолчанию используйте глобальные переменные RegExpr*.

•	(+) property ExtSyntaxEnabled заменено модификатором 'r' (русские диапазоны букв - подробнее см. описание)

•	(+) Perl-расширение (?#comment) - теперь можно вставлять комментарии внутрь выражений (жутко важное и сложное расширение)

•	(^) property MatchCount переименовано SubExprMatchCount. Я понимаю, что это может вызвать проблемы у пользователей предыдущих версий, но дело в том, что смысл этого свойства изменился и это переименование поможет Вам найти все места его использования и убедиться что и с новой реализацией все будет работать

•	(+) InputString property - хранит последнюю переданную в Exec строку. Можно присваивать значения напрямую для последующего вызова ExecPos.

•	(+) ExecPos method - для работы с хранимой в InputString строкой. Например InputString := AString; ExecPos; или InputString := AString; ExecPos (AOffset); Обратите внимание, что без параметра это работает только в Delphi 4 и старше. Смысл всего этого - не копировать каждый раз анализируемую строку.

•	(+) ExecNext method - позволяет легко и гораздо быстрее чем раньше найти все вхождения выражения во входную строку.

•	(^) параметр Offset удален из Exec method, если Вы его использовали, замените Exec (AString, AOffset) на InputString := AString; ExecPos (AOffset) Причина - прежняя реализация была мягко говоря неэффективной (строка каждый раз перегонялась во внутренний буфер TRegExpr что при обработке файлов просто недопустимо).

•	(^) исходя из предыдущего оптимизированы Substitution, Replace и Split

•	(-) небольшая ошибка - если случается ошибка при втором проходе компилятора, то  TRegExpr оставался в состоянии "успешно откомпилировано". Вообще-то мне трудно представить такую ситуацию, ошибка была найдена при теоретическом анализе исходников.

•	(-) более реальная ошибка - Dump не проверял состояние откомпилированности и соответственно выпадал по ошибке при вызове после неуспешной компиляции.

•	(+) существенно переработана обработка ошибок -см. functions Error, ErrorMsg, LastError, property CompilerErrorPos, type ERegExpr).

•	(-^) TRegExpr.Replace, Split и ExecNext зацикливались в том экзотическом случае (как правило связанным с неправильно сформулированным выражением), когда выражение совпадало с пустой строкой. Сейчас ExecNext перемещает указатель на MatchLen если MatchLen <> 0  и на +1 если MatchLen = 0 (Обнаружили Jon Smith и George Tasker).

•	(-) Совпадение с пустой строкой в конце строки (т.е. после последнего символа) не обнаруживалось. Сейчас это исправлено, однако это может вызвать проблемы в плохо продуманных программах, поскольку в таких случаях MatchPos[0]=length(AInputString)+1, MatchLen = 0). Если есть возражения/предложения - с удовольствием выслушаю.

•	(^) Доработана демо-программа

•	(^) Документация и новая версия помещена на мою домашнюю страничку http://anso.da.ru (http://anso.virtualave.net)

 v. 0.81 1999.12.25 // Merry Christmas ! :)

 -=- added \s (AnySpace) and \S (NotSpace) meta-symbols

     - implemented by Stephan Klimek with minor fixes by AVS

 -=- added \f, \a and \b chars (translates into FF, BEL, BS)

 -=- removed meta-symbols 'Ў' & 'г' - sorry for any inconvenients

 -=- added Match property (== copy (InputStr, MatchPos [Idx], MatchLen [Idx]))

 -=- added extra parameter Offset to Exec method

     (thanks to Steve Mudford)

 v. 0.7 1999.08.22

 -=- fixed bug - in some cases the r.e. [^...]

     incorrectly processed (as any symbol)

     (thanks to Jan Korycan)

 -=- Some changes and improvements in TestRExp.dpr

 v. 0.6 1999.08.13 (Friday 13 !)

 -=- changed header of TRegExpr.Substitute

 -=- added Split, Replace & appropriate

     global wrappers (thanks to Stephan Klimek for suggetions)

 v. 0.5 1999.08.12

 -=- TRegExpr.Substitute routine added

 -=- Some changes and improvements in TestRExp.dpr

 -=- Fixed bug in english version of documentation

     (Thanks to Jon Buckheit)

 v. 0.4 1999.07.20

 -=- Fixed bug with parsing of strings longer then 255 bytes

     (thanks to Guido Muehlwitz)

 -=- Fixed bug in RegMatch - mathes only first occurence of r.e.

     (thanks to Stephan Klimek)

 v. 0.3 1999.06.13

 -=- ExecRegExpr function

 v. 0.2 1999.06.10

 -=- packed into object-pascal class

 -=- code slightly rewriten for pascal

 -=- now macro correct proceeded in ranges

 -=- r.e.ranges syntax extended for russian letters ranges:

     р-  - replaced with all small russian letters (Win1251)

     L-- - replaced with all capital russian letters (Win1251)

     р-- - replaced with all russian letters (Win1251)

 -=- added macro '\d' (opcode ANYDIGIT) - match any digit

 -=- added macro '\D' (opcode NOTDIGIT) - match not digit

 -=- added macro '\w' (opcode ANYLETTER) - match any english letter or '_'

 -=- added macro '\W' (opcode NOTLETTER) - match not english letter or '_'

 (all r.e.syntax extensions may be turned off by flag ExtSyntax)

 v. 0.1 1999.06.09

 first version, with bugs, without help => must die :(


Установка, список файлов

Скопируйте RegExpr.pas в любой симпатичный Вам каталог и добавьте в список файлов Вашего проекта.

Все !

Теперь просто используйте класс TRegExpr или глобальные процедуры из regexpr.pas в Вашем проекте (см. примеры использования). Не забудьте добавить 'uses RegExpr' в модули, в которых это необходимо.

Файлы справки (в некоторых версиях TRegExpr распространяются отдельно)

•	  RegExpE.hlp, RegExpE.cnt - английский

•	  RegExpRu.hlp, RegExpRu.cnt - русский

•	  RegExpBg.hlp, RegExpBg.cnt - болгарский (спасибо Simeon Lilov)

•	  RegExpG.hlp, RegExpG.cnt - немецкий (спасибо Martin Baur)

•	  Справка на Вашем родном языке ;) Пожалуйста, помогите мне с переводом на другие языки !

Демо-программа состоит из файлов:

•	  TestRExp.dpr (собственно демо-проект)

•	  TestRE.pas (главная форма демо-проекта)

•	  TestRE.dfm

•	  PCode.pas (форма браузера для просмотра откомпилированного выражения ("P-code"))

•	  PCode.dfm

•	  FileViewer.pas (форма для просмотра результатов поиска выражения в файле, т.е. что-то типа утилиты egrep)

•	  FileViewer.dfm

•	  HyperLinkDecorator.pas (более сложные примеры)

•	  test.htm (файл для демонстрации поиска в нем выражений - см. FileViewer).


Примеры использования

Простейшие примеры

Если Вы не знакомы с регулярными выражениями, Вы можете почитать описание синтаксиса.

Использование глобальных процедур

 Это самый простой способ, но и самый неэффективный

ExecRegExpr ('\d{3}-(\d{2}-\d{2}|\d{4})', 'Phone: 555-1234');

   вернет True

ExecRegExpr ('^\d{3}-(\d{2}-\d{2}|\d{4})', 'Phone: 555-1234');

   вернет False, поскольку в выражении применен мета-символ '^' (начало строки), а во входной строке перед телефонным номером есть "посторонние" символы.

ReplaceRegExpr ('product', 'Take a look at product. product is the best !', 'TRegExpr');

   вернет строку 'Take a look at TRegExpr. TRegExpr is the best !'; ;)

Использование объектов TRegExpr

 Это дает доступ ко всем возможностям библиотеки

// Эта простая функция извлечет все адреса email из входной

// строки и вернет их перечень

function ExtractEmails (const AInputString : string) : string;

const

EmailRE = '[_a-zA-Z\d\-\.]+@[_a-zA-Z\d\-]+(\.[_a-zA-Z\d\-]+)+'

var

r : TRegExpr;

begin

Result := '';

r := TRegExpr.Create; // Создаем объект

try // гарантирует освобождение занятой объектом памяти

r.Expression := EmailRE;

// Присваиваем исходный текст регулярного выражения.

// При первой же необходимости (например, при вызове метода Exec)

// оно будет откомпилировано. Если в выражении есть ошибки, то

// будет вызвано исключение

if r.Exec (AInputString) then

REPEAT

Result := Result + r.Match [0] + ', ';

UNTIL not r.ExecNext;

finally r.Free;

end;

end;

begin

ExctractEmails ('My e-mails is anso@mail.ru and anso@usa.net');

// вернет 'anso@mail.ru, anso@usa.net, '

end.

// Обратите внимание, что если эта функция будет использоваться часто,

// то наша реализация далека от оптимальной.

// Правильнее тогда использовать заранее (при инициализации программы)

// созданный TRegExpr с уже откомпилированным выражением

// В этом примере мы извлечем телефонный номер из входной строки,

// разделим его на составляющие (код страны, код города, внутренний номер телефона).

// Затем подставим все это в шаблон.

function ParsePhone (const AInputString, ATemplate : string) : string;

const

IntPhoneRE = '(\+\d *)?(\(\d+\) *)?\d+(-\d*)*';

var

r : TRegExpr;

begin

r := TRegExpr.Create; // Создаем объект

try // гарантирует освобождение занятой объектом памяти

r.Expression := IntPhoneRE;

// Присваивая этому свойству регулярное выражение

// мы тем самым его компилируем. Если в выражении

// есть ошибки, то будет сгенерирована исключительная ситуация

if r.Exec (AInputString)

then Result := r.Substitute (ATemplate)

else Result := '';

finally r.Free;

end;

end;

begin

ParsePhone ('Phone of AlkorSoft (project PayCash) is +7(812) 329-44-69',

'Zone code $1, city code $2. Whole phone number is $&.');

// вернет 'Zone code +7, city code (812) . Whole phone number is +7(812) 329-44-69.'

end.

Более сложные примеры

Вы можете также познакомится с другими примерами использования TRegExpr в демо-программке TestRExp.dpr а также в библиотеке для преобразования текста в HTML HyperLinkDecorator.pas.

Не забудьте также прочитать мои статьи на Delphi3000.com (К сожалению, только на английском) и Delphi Kingdom, и проголосовать там за эти статьи ;).

Детальное описание

Пожалуйста, ознакомьтесь с полным описанием интерфейса TRegExpr.


Синтаксис регулярных выражений

Введение

Регулярные выражения - это широкоиспользуемый способ описания шаблонов для поиска текста и проверки соответствия текста шаблону. Специальные метасимволы позволяют определять, например, что Вы ищете подстроку в начале входной строки или определенное число повторений подстроки.

На первый взгляд регулярные выражения выглядят страшновато (ну хорошо, на второй - еще страшнее ;) ).

Однако Вы очень быстро оцените всю их мощь. Они съэкономят Вам многие часы ненужного кодирования, а в некоторых случаях будут и быстрее работать, чем вручную закодированные проверки.

Я настоятельно рекомендую Вам "поиграть" с поставляемой в дистрибутиве демо-программой TestRExp.dpr - это позволит Вам лучше понять принцип работы регулярных выражений и отладить Ваши собственные выражения. Кроме того, в TestRExp входит множество примеров выражений.

Давайте начнем наше знакомство с регулярными выражениями!

Простое сравнение

Любой символ совпадает с самим собой, если он не относится к специальным метасимволам описанным чуть ниже.

Последовательность символов совпадает с такой же последовательностью во входной строке, так что шаблон "bluh" совпадет с подстрокой "bluh'' во входной строке. Пока все просто, не так ли ?

Если необходимо, чтобы метасимволы или escape-последовательности воспринимались как обычные символы, их нужно предварять символом "\", например, метасимвол "^" обычно совпадает с началом строк, однако, если записать его как "\^", то он будет совпадать с символом "^", "\\" совпадает с "\" и т.д.

Примеры:

  foobar          находит 'foobar'

  \^FooBarPtr     находит '^FooBarPtr'

Escape-последовательности

Любой символ может быть определен с помощью escape последовательности, так же как это делается в языках C или Perl: "\n'' означает начало строки, "\t'' - табуляцию и т.д.. Вообще, \xnn, где nn это последовательность шестнадцатеричных цифр, означает символ с ASCII-кодом nn. Если необходимо определить двухбайтный (Unicode) символ, используйте формат '\x{nnnn}', где 'nnnn' - одна или более шестнадцатеричных цифр.

  \xnn     символ с шестнадцатеричным кодом nn

  \x{nnnn} символ с шестнадцатеричным кодом nnnn (более одного байта можно задавать только в режиме Unicode)

  \t       табуляция (HT/TAB), можно также \x09

  \n       новая строка (NL), можно также \x0a

  \r       возврат каретки (CR), можно также \x0d

  \f       перевод формата (FF), можно также \x0c

  \a       звонок (BEL), можно также \x07

  \e       escape (ESC), можно также \x1b

Примеры:

  foo\x20bar   находит 'foo bar' (обратите внимание на пробел посередине)

  \tfoobar     находит 'foobar' предшествуемый табуляцией

Перечни символов

Вы можете определить перечень, заключив символы в []. Перечень будет совпадать с любым одним символом перечисленным в нем.

Если первый символ перечня (сразу после "['') - "^'', то такой перечень совпадает с любым символом не перечисленным в перечне.

Примеры:

  foob[aeiou]r   находит 'foobar', 'foober'  и т.д. но не 'foobbr', 'foobcr' и т.д.

  foob[^aeiou]r  находит 'foobbr', 'foobcr'  и т.д.. но не 'foobar', 'foober'  и т.д.

Внутри перечня символ "-'' может быть использован для определения диапазонов символов, например a-z представляет все символы между "a'' и "z'', включительно.

Если Вам необходимо включить в перечень сам символ "-'', поместите его в начало или конец перечня или предварите '\'. Если Вам необходимо поместить в перечень сам символ ']', поместите его в самое начало или предварите '\'.

Примеры:

  [-az]      'a', 'z' и '-'

  [az-]      'a', 'z' и '-'

  [a\-z]     'a', 'z' и '-'

  [a-z]      все 26 малых латинских букв от 'a' до 'z'

  [\n-\x0D]  #10, #11, #12, #13.

  [\d-t]     цифра, '-' или 't'.

  []-a]      символ из диапазона ']'..'a'.

Метасимволы

Метасимволы - это специальные символы, являющиеся важнейшим понятием в регулярных выражениях. Существует несколько групп метасимволов.

Метасимволы - разделители строк

  ^      начало строки

  $      конец строки

  \A     начало текста

  \Z     конец текста

  .      любой символ в строке

Примеры:

  ^foobar     находит 'foobar' только если он в начале строки

  foobar$     находит 'foobar' только если он в конце строки

  ^foobar$    находит 'foobar' только если это единственное слово в строке

  foob.r      находит 'foobar', 'foobbr', 'foob1r'  и т.д.

Метасимвол "^" по умолчанию совпадает только в начале входного текста, а метасимвол "$"  - только в конце текста. Внутренние разделители строк, имеющиеся в тексте, не будут совпадать с "^'' и "$''.

Однако, если Вам необходимо работать с текстом как с многострочным, чтобы "^'' совпадал после каждого разделителя строки внутри текста, а  "$'' - перед каждым разделителем, то Вы можете включить модификатор /m.

Метасимволы \A и \Z аналогичны "^'' и "$'', но на них не действует модификатор /m, т.е. они всегда совпадают только с началом и концом всего входного текста.

Метасимвол ".'' по умолчанию совпадает с любым символом, однако, если Вы выключите модификатор /s, то '.' не будет совпадать с разделителями строк.

TRegExpr интерпретирует разделители строк так, как это рекомендовано на www.unicode.org ( http://www.unicode.org/unicode/reports/tr18/ ):

 "^" совпадает с началом входного текста, а также, если включен модификатор /m, с точкой непосредственно следующей после  \x0D\x0A, \x0A или \x0D (если Вы используете Unicode-версию TRegExpr, то также \x2028 или  \x2029 или \x0B или \x0C или \x85). Обратите внимание, что он не совпадает в промежутке внутри последовательности \x0D\x0A.

"$" совпадает с концом входного текста, а также, если включен модификатор /m, с точкой непосредственно предшествующей \x0D\x0A, \x0A или \x0D (если Вы используете Unicode-версию TRegExpr, то также \x2028 или  \x2029 или \x0B или \x0C или \x85). Обратите внимание, что он не совпадает в промежутке внутри последовательности \x0D\x0A.

"." совпадает с любым символом, но если выключен модификаторr /s, то "." не совпадает с \x0D\x0A и \x0A и \x0D (если Вы используете Unicode-версию TRegExpr, то не совпадает также с \x2028 и  \x2029 и \x0B и \x0C и \x85).

Обратите внимание, что "^.*$" (шаблон для пустой строки) не совпадает с пустой строкой вида \x0D\x0A, но совпадает с \x0A\x0D.

Вы можете перенастроить вышеописанное поведение при обработке многострочных текстов - см. описания свойств LineSeparators и LinePairedSeparator, скажем, Вы можете перенастроиться на использование только Unix-разделителей строк \n или только DOS/Windows-разделителей \r\n или же смешанных разделителей (так и настроено по умолчанию) или вообще определить свои собственные разделители строк!

Метасимволы - стандартные перечни символов

  \w     буквенно-цифровой символ или "_"

  \W     не \w

  \d     цифровой символ

  \D     не \d

  \s     любой "пробельный" символ (по умолчанию - [ \t\n\r\f])

  \S     не \s

Стандартные перечни \w, \d и \s можно использовать и внутри перечней символов.

Примеры:

  foob\dr     находит 'foob1r', ''foob6r'  и т.д. но не 'foobar', 'foobbr'  и т.д.

  foob[\w\s]r находит 'foobar', 'foob r', 'foobbr'  и т.д. но не 'foob1r', 'foob=r'  и т.д.

TRegExpr использует свойства SpaceChars и WordChars для того, чтобы определять стандартные перечни \w, \W, \s, \S, т.е. Вы легко можете переопределить состав этих перечней.

Метасимволы - повторения

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

  *      ноль или более раз ("жадный"), то же что {0,}

  +      один или более раз ("жадный"), то же что {1,}

  ?      ноль или один раз ("жадный"), то же что {0,1}

  {n}    точно n раз ("жадный")

  {n,}   не менее n раз ("жадный")

  {n,m}  не менее n но не более m раз ("жадный")

  *?     ноль или более раз ("не жадный"), то же что {0,}?

  +?     один или более раз ("не жадный"), то же что {1,}?

  ??     ноль или один раз ("не жадный"), то же что {0,1}?

  {n}?   точно n раз ("не жадный")

  {n,}?  не менее n раз ("не жадный")

  {n,m}? не менее n но не более m раз ("не жадный")

Т.о. {n,m} задает минимум n повторов и максимум - m. Повторитель {n} эквивалентен {n,n} и задает точно n повторов. Повторитель {n,} задает минимум n повторов. Теоретически величина параметров n и m не ограничена, но рекомендуется не задавать большие значения, поскольку в некоторых ситуациях это может потребовать существенных затрат времени и ОЗУ при обработке такого повторителя в связи с рекурсивным характером работы.

Если фигурные скобки встречаются в "неправильном" месте, где они не могут быть восприняты как повторитель, то они вопринимаются просто как символы.

Примеры:

  foob.*r     находит 'foobar',  'foobalkjdflkj9r' и 'foobr'

  foob.+r     находит 'foobar', 'foobalkjdflkj9r' но не 'foobr'

  foob.?r     находит 'foobar', 'foobbr' и 'foobr' но не 'foobalkj9r'

  fooba{2}r   находит 'foobaar'

  fooba{2,}r  находит 'foobaar', 'foobaaar', 'foobaaaar' и т.д.

  fooba{2,3}r находит 'foobaar', или 'foobaaar'  но не 'foobaaaar'

Небольшое пояснение по поводу "жадности". "Жадные" варианты повторителей пытаются захватить как можно большую часть входного текста, в то время как "не жадные" - как можно меньшую. Например, 'b+' как и 'b*' примененные к входной строке 'abbbbc' найдут 'bbbb', в то время как 'b+?' найдет только 'b', а 'b*?' - вообще - пустую строку; 'b{2,3}?' найдет 'bb', в то время как 'b{2,3}' найдет 'bbb'.

Вы можете переключить все повторители в выражении в "не жадный" режим, воспользовавшись модификатором /g.

Метасимволы - варианты

Вы можете определить перечень вариантов, используя метасимвол "|'' для их разделения, например "fee|fie|foe" найдет "fee'' или "fie'' или "foe'', (так же как "f(e|i|o)e"). В качестве первого варианта воспринимается все от предыдущего метасимвола "('' или "['' или от начала выражения до первого метасимвола "|'', в качестве последнего - все от последнего "|'' до конца выражения или до ближайшего метасимвола ")''. Обычно, чтобы не запутаться, набор вариантов всегда заключают в скобки, даже если без этого можно было бы обойтись.

Варианты пробуются начиная с первого и попытки завершаются сразу же как удастся подобрать такой при котором совпадет вся последующая часть выражения (подробнее см.Механизм работы). Это означает, что варианты не обязательно обеспечат "жадное" поведение. Например, если применить выражение "foo|foot" ко входной строке "barefoot'', то будет найдено "foo'' так это первый вариант который позволил совпасть всему выражению.

Обратите внимание, что метасимвол "|'' воспринимается как обычный символ внутри перечней символов, например, [fee|fie|foe] означает ровно то же самое что и [feio|].

Примеры:

  foo(bar|foo)  находит 'foobar' или 'foofoo'.

Метасимволы - подвыражения

Метасимволы ( ... ) могут также использоваться для задания подвыражений - по завершении поиска выражения Вы можете обратиться к любому подвыражению используя свойства MatchPos, MatchLen и Match, а также подставлять подвыражения в некий шаблон, используя метод Substitute).

Подвыражения нумеруются слева направо, в порядке появления открывающих скобок.

Первое подвыражение имеет номер '1' (выражение в целом - '0', к нему можно обращаться в Substitute как '$0' так и '$&').

Примеры:

  (foobar){8,10}  находит строку содержащую 8, 9 или 10 копий 'foobar'

  foob([0-9]|a+)r находит 'foob0r', 'foob1r' , 'foobar', 'foobaar', 'foobaar'  и т.д.

Метасимволы - обратные ссылки

Метасимволы от \1 до \9 воспринимаются как обратные ссылки. \<n> совпадает с ранее найденным подвыражением #<n>.

Примеры:

  (.)\1+         находит 'aaaa' и 'cc'.

  (.+)\1+        также находит 'abab' и '123123'

  (['"]?)(\d+)\1 находит "13" (в дв.кавычках), или '4' (в один.кавычках) или 77 (без кавычек) и т.д.

Модификаторы

Модификаторы служат для изменения режимов работы TRegExpr.

Вы можете изменять модификаторы несколькими способами.

Любой модификатор может меняться с помощью специальной конструкции (?...) внутри регулярного выражения.

Также, Вы можете присвоить значение соответствующему свойству экземпляра объекта TRegExpr (например, ModifierX для изменения модификатора /x, или ModifierStr для изменения сразу нескольких модификаторов). Значения по умолчанию для новых экземпляров объектов TRegExpr определены в глобальных константах, например RegExprModifierX определяет значение по умолчанию для ModifierX.

i

Регистро-независимый режим (по умолчанию использует выбранный в ОС язык по умолчанию), (см. также InvertCase)

m

Воспринимать входной текст как многострочный, при этом метасимволы "^'' и "$'' будут совпадать не только в начале и конце текста в целом, но и в начале и в конце всех имеющихся в тексте строк (см. также Разделители строк)

s

Воспринимать входной текст как одну строку. При этом метасимвол ".'' совпадает с любым символом, если же этот модификатор выключен, то он не совпадает с разделителями строк (см.также Разделители строк).

g

Не стандартный модификатор. Выключая его Вы переключаете все повторители в "не жадный" режим (по умолчанию этот модификатор включен). Т.е. если его отключить, то все '+' работают как '+?', '*' как '*?' и т.д.

x

Позволяет форматировать шаблон чтобы обеспечить более легкую читаемость (см. описание ниже).

r

Не стандартный модификатор. Если включен, то диапазоны вида а-я включают также букву 'ё', А-Я  включают 'Ё', а а-Я включает вообще все русские буквы.

Модификатор /x заставляет TRegExpr игнорировать пробелы, табуляции и разделители строк, что позволяет форматировать текст выражения. Кроме того, если встречается символ #, то все последующие символы до конца строки воспринимаются как комментарий, например:

(

(abc) # Комментарий 1

  |   # Пробелы внутри выражения также игнорируются

(efg) # Комментарий 2

)

Естественно, это означает что, если Вам нужно вставить в выражение пробел, табуляцию или разделитель строки или #, то в расширенном (/x) режиме это можно сделать только предваряя их '/' или используя /xnn (внутри перечней символов все эти символы воспринимаются как обычно)

Расширения Perl

(?imsxr-imsxr)

Позволяет изменять значения модификаторов

Примеры:

  (?i)Saint-Petersburg       находит 'Saint-petersburg' и 'Saint-Petersburg'

  (?i)Saint-(?-i)Petersburg  находит 'Saint-Petersburg' но не 'Saint-petersburg'

  (?i)(Saint-)?Petersburg    находит 'Saint-petersburg' и 'saint-petersburg'

  ((?i)Saint-)?Petersburg    находит 'saint-Petersburg', но не 'saint-petersburg'

(?#text)

Комментарий, просто игнорируется. Обратите внимание, что в комментарии такого вида невозможно поместить символ ")", поскольку он воспринимается как конец комментария.

Внутренний механизм работы

Вам любопытно, как устроен TRegExpr изнутри?

К сожалению, этот раздел пока что в стадии написания, подождите немного!

А пока не забудьте заглянуть в ЧАВО (особенно на вопрос о нежадном режиме).


Интерфейс

Методы и свойства TRegExpr:

property Expression : string;

Собственно регулярное выражение.

Для ускорения работы TRegExpr автоматически выполняет компиляцию выражения во внутреннее представления (его можно посмотреть через Dump).

Однако, делается это только тогда, когда это реально необходимо, т.е. при обращении к методам Exec[Next], Substitute, Dump и т.п. и только в том случае, если после последней [пере]компиляции было изменено свойство Expression или какие-либо другие влияющие на откомпилированное выражение свойства.

При ошибках компиляции вызывается метод Error (по умолчанию он генерирует исключение ERegExpr - см. ниже)

property ModifierStr : string;

Проверка и установка модификаторов с помощью строки в том же формате, что и в конструкции (?ismx-ismx). Т.е., например ModifierStr := 'i-x' включит регистро-независимый режим и выключит режим расширенного синтаксиса, прочие модификаторы останутся без изменений.

Если указать несуществующий модификатор, вызывается Error

property ModifierI : boolean;

Модификатор /i - ("регистро-независимый режим"), инициализируется из RegExprModifierI

property ModifierR : boolean;

Модификатор /r ("русские диапазоны"), инициализируется из RegExprModifierR

property ModifierS : boolean

Модификатор /s - если установлен, то '.' совпадает с любым символом, (если сброшен, то '.' не совпадает с LineSeparators и LinePairedSeparator, , инициализируется из RegExprModifierS

property ModifierG : boolean;

Модификатор /g, отключение приводит к тому, что все операторы работают в "не жадном" (non-greedy) режиме, т.е. когда ModifierG = False, то все '*' работают как '*?', все '+' как '+?' и т.д.., инициализируется из RegExprModifierG

property ModifierM : boolean;

Модификатор /m -воспринимать входной текст как многострочный. Если выключен, то метасимволы `^' и `$' "срабатывают" только в начале и конце входного текста.

Если включен, то эти символы срабатывают также и в начале и в конце каждой строки входного текста., инициализируется из RegExprModifierM

property ModifierX : boolean;

Модификатор /x - ("расширенный синтаксис"), инициализируется из RegExprModifierX

function Exec (const AInputString : string) : boolean;

Выполнить выражение применительно к входной строке AInputString

!!! также, запоминает AInputString в ствойстве InputString

function ExecNext : boolean;

поиск следующего совпадения. Фактически:

    Exec (AString); ExecNext;

 означает то же что и

    Exec (AString);

    if MatchLen [0] = 0 then ExecPos (MatchPos [0] + 1)

     else ExecPos (MatchPos [0] + MatchLen [0]);

 но воспринимается гораздо нагляднее!

function ExecPos (AOffset: integer = 1) : boolean;

выполняет выражение для строки в InputString начиная с позиции AOffset

(AOffset=1 - первый символ InputString)

property InputString : string;

текущая входная строка (присвоенная явно или в последнем Exec).

Присвоение этому свойству значений делает неопределенными свойства Match* !

function Substitute (const ATemplate : string) : string;

Возвращает ATemplate в котором все '$&' и '$0' заменены на найденное регулярное выражение, а '$n' заменены на подвыражения #n.

Начиная с версии v.0.929 используется '$' вместо '\' (для расширений типа \n\r и т.п. а также для большей схожести с Perl) и допускаются n > 9.

Если Вам необходим просто символ '$' или '\', предваряйте их '\'.

Например: '1\$ is $2\\rub\\' -> '1$ is <Match[2]>\rub\'

Если Вам необходимо сразу после '$n' поместить цифру, заключайте n в фигурные скобки '{}'.

Например: 'a$12bc' -> 'a<Match[12]>bc', 'a${1}2bc' -> 'a<Match[1]>2bc'.

procedure Split (AInputStr : string; APieces : TStrings);

Режет входную строку AInputStr на помещаемые в APieces куски разделяемые вхождениями выражения. Внимание! Этот метод вызывает методы Exec[Next]

function Replace (AInputStr : string; const AReplaceStr : string) : string;

Заменяет в AInputStr все вхождения выражения на AReplaceStr

Внимание! Этот метод вызывает методы Exec[Next]

property SubExprMatchCount : integer; // ReadOnly

Число подвыражений, найденных в последнем Exec*. если найдено только само выражение в целом, то SubExprMatchCount=0, если и само выражение не найдено (Exec* вернул false) то SubExprMatchCount=-1. Обратите внимание, что часть подвыражений может быть не найдено и для них MathPos=MatchLen=-1 и Match=''.

Например, для Expression := '(1)?2(3)?';

Exec ('123'): SubExprMatchCount=2, Match[0]='123', [1]='1', [2]='3'

Exec ('12'): SubExprMatchCount=1, Match[0]='12', [1]='1'

Exec ('23'): SubExprMatchCount=2, Match[0]='23', [1]='', [2]='3'

Exec ('2'): SubExprMatchCount=0, Match[0]='2'

Exec ('7') - возвращает False: SubExprMatchCount=-1

property MatchPos [Idx : integer] : integer; // ReadOnly

Позиция начала подвыражения #Idx во входной строке

Первое подвыражение имеет Idx=1, последнее - MatchCount, выражение в целом  Idx=0.

Возвращает -1 если нет такого подвыражения или если оно не найдено во входной строке.

property MatchLen [Idx : integer] : integer; // ReadOnly

Длина подвыражения #Idx во входной строке

Первое подвыражение имеет Idx=1, последнее - MatchCount, выражение в целом  Idx=0.

Возвращает -1 если нет такого подвыражения или если оно не найдено во входной строке.

property Match [Idx : integer] : string; // ReadOnly

== copy (InputString, MatchPos [Idx], MatchLen [Idx])

Возвращает '' если нет такого подвыражения или если оно не найдено во входной строке.

function LastError : integer;

Код последней ошибки, 0 если нет ошибки (бессмысленно использовать эту функцию, если Вы не изменяете реализацию Error, поскольку текущая реализация генерирует исключительную ситуацию).

Вызов этой функции очищает внутреннюю переменную и повторный вызов всегда вернет 0.

function ErrorMsg (AErrorID : integer) : string; virtual;

Возвращает текст сообщения об ошибке с кодом AErrorID.

property CompilerErrorPos : integer; // ReadOnly

Возвращает позицию, в которой случилась последняя ошибка компиляции (упрощает отладку выражений)

property SpaceChars : RegExprString

Содержит символы, трактуемые как \s (инициализируется из глобальной константы RegExprSpaceChars)

property WordChars : RegExprString;

Содержит символы, трактуемые как \w (инициализируется из глобальной константы RegExprWordChars)

property LineSeparators : RegExprString

Разделители строк (например, \n для Unix) (инициализируется из глобальной константы RegExprLineSeparators)

см.подробнее о разделителях строк

property LinePairedSeparator : RegExprString

Сдвоенный разделитель строк (как, \r\n для DOS и Windows) (инициализируется из глобальной константы RegExprLinePairedSeparator)

см.подробнее о разделителях строк

Например, если Вам необходимо отслеживать только Unix-разделители строк, присвойте LineSeparators := #$a (символ новой строки) и LinePairedSeparator := '' (пустую строку), если необходимо воспринимать как разделители строк только \x0D\x0A но не отдельные \x0D или \x0A, присвойте LineSeparators := '' (пустую строку) и LinePairedSeparator := #$d#$a.

По умолчанию используется "смешанный вариант" (им инициализированы константы RegExprLine[Paired]Separator[s]): LineSeparators := #$d#$a; LinePairedSeparator := #$d#$a подробно описанный в описании синтаксиса.

class function InvertCaseFunction  (const Ch : REChar) : REChar;

Преобразует символ Ch в верхний регистр если это символ нижнего регистра и в нижний - если верхнего (используются текущие установки операционной системы)

property InvertCase : TRegExprInvertCaseFunction;

Позволяет определить свою собственную реализацию регистро-независимого режима работы TRegExpr. Инициализируется из глобальной константы RegExprInvertCaseFunction (по умолчанию она указывает на InvertCaseFunction)

procedure Compile;

Вызывает принудительную [пере]компиляцию  регулярного выражения.

Может быть полезной, например, для проверки корректности всех свойств при создании визуальных редакторов рег.выражений и т.п.

function Dump : string;

возвращает внутренний формат в который было откомпилировано выражение. Предназначено для особо любознательных ;)

Глобальные константы

Значения по умолчанию для модификаторов:

  RegExprModifierI : boolean = False;	// TRegExpr.ModifierI

  RegExprModifierR : boolean = True;	// TRegExpr.ModifierR

  RegExprModifierS : boolean = True;	// TRegExpr.ModifierS

  RegExprModifierG : boolean = True;	// TRegExpr.ModifierG

  RegExprModifierM : boolean = False;	// TRegExpr.ModifierM

  RegExprModifierX : boolean = False;	// TRegExpr.ModifierX

  RegExprSpaceChars : RegExprString // Значение по умолчанию для SpaceChars

    = ' '#$9#$A#$D#$C;

  RegExprWordChars : RegExprString // Значение по умолчанию для WordChars

   =  '0123456789'

  + 'abcdefghijklmnopqrstuvwxyz'

  + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_';

  RegExprLineSeparators : RegExprString // Значение по умолчанию для LineSeparators

   =  #$d#$a{$IFDEF UniCode}#$b#$c#$2028#$2029#$85{$ENDIF};

  RegExprLinePairedSeparator : RegExprString // Значение по умолчанию для LinePairedSeparator

   = #$d#$a;

  RegExprInvertCaseFunction : TRegExprInvertCaseFunction // Значение по умолчанию для InvertCase

 = TRegExpr.InvertCaseFunction;

Глобальные функции

function ExecRegExpr (const ARegExpr, AInputStr : string) : boolean;

true если строка AInputString совпадает с выражением ARegExpr

! При ошибках в ARegExpr будет генерировать exception

procedure SplitRegExpr (const ARegExpr, AInputStr : string; APieces : TStrings);

Режет AInputStr на помещаемые в APieces куски по вхождениям выражения ARegExpr (например, разбиение строки на отдельные поля, разделенные некой последовательностью символов)

function ReplaceRegExpr (const ARegExpr, AInputStr, AReplaceStr : string) : string;

Возвращает AInputStr в которой все вхождения выражения ARegExpr заменены на AReplaceStr

function QuoteRegExprMetaChars (const AStr : string) : string;

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

Например 'abc$cd.(' преобразуется в 'abc\$cd\.\('

Эта функция полезна для автоматического синтеза регулярного выражения на основании пользовательских данных

function RegExprSubExpressions (const ARegExpr : string;

 ASubExprs : TStrings; AExtendedSyntax : boolean = False) : integer;

Создает список найденные в рег.выражении ARegExpr подвыражений

Каждому подвыражению соответствует элемент в ASubExps, где

  String - исходный текст подвыражения (без обрамляющих '()')

  мл.слово Object - начальная позиция подвыражения в  ARegExpr, включая обрамляющий '(' если он есть!

  ст.слово Object - длина подвыражения, включая '(' и ')' если они есть!

AExtendedSyntax - должен быть True если модификатор /x будет включен при использовании данного регулярного выражения.

Эта функция полезна для написания визуальных редакторов рег.выражений и т.п. Пример использования есть в TestRExp.dpr

Результат	Комментарий

0 	Успех. Все скобки сбалансированы;

-1 	Не хватает как минимум одной закрывающей скобки ')';

-(n+1) 	В позиции n обнаружен незакрытый '[';

n 	В позиции n обнаружена закрывающая ')' для которой нет открывающей '('.

Возвращает 0 если все скобки сбалансированы, или -1 если недостаточно закрывающих скобок ')', или n если в позиции n встречена закрывающая скобка ')' которой не соответствует ни одна открывающая '('.

Естественно, если Result <> 0, то ASubExprs может быть некорректен.

Тип генерируемого при ошибках Exception

Обработчик ошибок TRegExpr по умолчанию (Вы вполне можете его перекрыть и изменить его поведение) генерирует exception:

ERegExpr = class (Exception)

   public

    ErrorCode : integer; // Код ошибки. Ошибки компиляции выражения меньше 1000, что позволяет их отличить от ошибок выполнения выражения.

    CompilerErrorPos : integer; // Позиция в выражении где произошла последняя ошибка компиляции выражения

  end;

Как использовать Unicode

TRegExpr теперь поддерживает работу с UniCode.

Обратите внимание, что этот режим практически неоптимизирован и работает чрезвычайно медленно (по сравнению со стандартным режимом).

Используйте его только если Вам действительно не обойтись без UniCode (а лучше примите участие в разработке TRegExpr и оптимизируйте UniCode-режим).

Чтобы переключить TRegExpr на работу с unicode уберите '.' из {.$DEFINE UniCode} в файле regexpr.pas.

Все строки после этого будут восприниматься как WideString.


Демо-программа

Поставляется в откомпилированном виде (TestRExp.exe) а также в исходных текстах (проект TestRExp.dpr).

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

Обратите внимание, что если Вы используете Delphi версии 3 и ниже, то при открытии этого проекта Вы получите серию предупреждений о несуществующих свойствах. Это не нарушит работу программы (речь идет о расширениях, появившихся в Delphi 4 и позволяющих более интеллектуально изменять размеры и положение компонентов при изменении размеров содержащей их формы).

Используя эту программу, Вы сможете легко определять номер редактируемого подвыражения, выделять выбранное подвыражение как в исходном тексте регулярного выражения, так и в результатах поиска во входной строке, проверять результаты работы функций Substitude, Replace и Split и т.п.

Кроме того, в эту программу встроено множество примеров, используя которые Вы упростите себе процесс изучения синтаксиса регулярных выражений, а также сможете легко и быстро получить представление о возможностях TRegExpr.


Пример - оформление гипер-ссылок

DecorateURLs   DecorateEMails

Эти функции полезны для преобразования некоего текста, содержащего гипер-ссылки в  HTML-код.

Например, подстрока 'http://anso.da.ru' будет заменена на '<a href="http://anso.da.ru">anso.da.ru</a>', а подстрока 'anso@mail.ru' заменится на '<a href="mailto:anso@mail.ru">anso@mail.ru</a>'.

function DecorateURLs

Оформляет ссылки найденные как по сигнатуре 'http://...' или 'ftp://..' так и ссылки в которых протокол не указан, но они начинаются с 'www.' Прим. если нужно также оформить как ссылки e-mail адреса, воспользуйтесь функцией DecorateEMails.

function DecorateURLs (const AText : string; AFlags : TDecorateURLsFlagSet = [durlAddr, durlPath]) : string;

Описание

Возвращает текст AText с оформленными гипер-ссылками.

AFlags определяет, какая часть гипер-ссылки будет помещена в видимую часть. Например, если указать [durlAddr] то гипер-ссылка 'http://anso.da.ru/index.htm' будет оформлена как '<a href="http://anso.da.ru/index.htm">anso.da.ru</a>'.

type

 TDecorateURLsFlags = (durlProto, durlAddr, durlPort, durlPath, durlBMark, durlParam);

 TDecorateURLsFlagSet = set of TDecorateURLsFlags;

Описание

Возможные значения:

Значение	Описание

 durlProto	Протокол ('ftp://' или 'http://')

durlAddr	TCP адрес или доменное имя сервера (например, 'anso.da.ru')

durlPort	Номер порта, если указан  (например, ':8080')

durlPath	Путь к файлу (например, 'index.htm')

durlBMark	Закладка (например, '#mark')

durlParam	URL-параметры (например, '?ID=2&User=13')

function DecorateEMails

Заменяет все обнаруженные адреса e-mails на гипер-ссылки вида '<a href="mailto:ADDR">ADDR</a>'. Например, адрес 'anso@mail.ru' будет заменен на '<a href="mailto:anso@mail.ru">anso@mail.ru</a>'.

function DecorateEMails (const AText : string) : string;

Описание

Возвращает текст AText с оформленными как гипер-ссылки адресами e-mails


ЧАВО
ЧАсто задаваемые ВОпросы

В.

Как использовать TRegExpr в Borland C++ Builder?

Я не могу это сделать, потому что нет заголовочных файлов (.h или .hpp).

О.

•	Добавьте RegExpr.pas в Ваш bcb-проект

•	Откомпилируйте проект. В результате автоматически будет создан hpp-файл (сообщения об ошибках можно проигнорировать)

•	Теперь Вы можете использовать класс TRegExpr в своем проекте. Не забывайте добавлять #include "RegExpr.hpp" в соответствующие cpp-файлы

В.

Почему TRegExpr возвращает более одной строки?

Например, выражение <font .*> возвращает <font, а затем - весь оставшийся файл включая завершающий </html>...

О.

Для обеспечения обратной совместимости, модификатор /s по умолчанию включен.

Если Вы его выключите, то мета-символ '.' перестанет совпадать с разделителями строк - и Вы получите ожидаемый Вами результат.

Кстати, данное конкретное выражение было бы эффективнее переписать как '<font ([^\n>]*)>', (в Match[1] Вы получите URL).

В.

Почему TRegExpr возвращает больше чем я ожидал?

Например, выражение '<p>(.+)</p>' примененное к строке '<p>a</p><p>b</p>' возвращает 'a</p><p>b' а не 'a' как я ожидал.

О.

По умолчанию, все операторы работают в "жадном" ('greedy') режиме и пытаются захватить как можно большую часть входной строки.

Если Вам необходим "не жадный" ('non-greedy') режим, то Вы можете использовать либо специальные "не жадные" варианты операторов ('+?' и т.п.) либо вообще переключить модификатор "жадности" ModifierG. Прим.: эта возможность появилась начиная с версии 0.940).

В.

Как анализировать HTML с помощью TRegExpr

О.

Мне очень часто задают этот вопрос! Должен вас огорчить, корректный ответ - "никак".

Конечно, вполне возможно (и очень удобно) использовать TRegExpr для извлечения каких-то специфичных фрагментов HTML, я сам часто этим пользуюсь, но если Вам нужен полноценный синтаксический разбор, то возьмите подходящий инструмент - синтаксический анализатор, а не регулярные выражения!

Мне не хочется занимать здесь место объяснениями почему это так - просто поверьте или почитайте например книжку Tom Christiansen и Nathan Torkington 'Perl Cookbook' (в русском переводе вышла в серии "Библиотека программиста" и называется просто "Perl"). Если в двух словах - мало того что отдельные случаи даже теоретически невозможно обработать с помощью регулярных выражений, так еще и не стоит забывать, что регулярные выражения это достаточно ресурсоемкий механизм выполняющий оптимизационный поиск, в то время как синтаксический анализ обычно линеен и работает гораздо быстрее. Используйте подходящие инструменты для каждого вида работ!

В.

Как мне получить все вхождения регулярного выражения а не только первое?

О.

Очень просто - напишите цикл, который будет вызывать метод ExecNext и тем самым переберет все вхождения.

Пример Вы можете посмотреть в реализации метода TRegExpr.Replace или в исходных текстах модуля HyperLinksDecorator.pas

В.

Я хочу проверить вводимые пользователем строки. Но почему-то TRegExpr иногда возвращает True для явно ошибочных строк.

О.

Возможно, Вы просто забыли о том, что регулярные выражения ИЩУТ заданный Вами шаблон во входной строке. Поэтому, если например задать шаблон типа '\d{4,4}' то он "сработает" не только для строк, состоящих из четырех цифр, но и для таких строк как '12345' или даже  'что угодно 1234 и опять что угодно'. Если Вам необходимо убедиться что во входной строке ТОЛЬКО искомый шаблон, не забудьте обрамлять выражение метасимволами начала и конца строки: '^\d{4,4}$'.

В.

Почему "не жадные" повторители иногда ведут себя "жадно"?

Например, выражение 'a+?,b+?', примененное к строке 'aaa,bbb' находит 'aaa,b', в то время как я ожидал что будет найдено 'a,b', ведь первый повторитель написан в "нежадной" форме!

О.

Это особенность используемой TRegExpr (а также Perl-ом и многими Unix-скими регулярными выражениями) математики - выполняется только "простая" оптимизация при поиске и не делается попыток найти наиболее оптимальный вариант. В некоторых ситуациях это неудобно, однако, в основном, это можно воспринимать как преимущество а не недостаток, поскольку это обеспечивает большую скорость работы и предсказуемость результатов.

Основное правило - вначале TRegExpr пытается найти соответствие выражению начиная с текущей позиции во входном тексте. И только если это абсолютно невозможно, передвигается на символ вперед и повторяет все сначала. Поэтому, для выражения 'a,b+?' будет найдено 'a,b', а вот в случае 'a+?,b+?' выражение не рекомендует (с помощью "нежадной" формы повторителя) но и не запрещает вовсе совпадение более одного символа 'a', поэтому TRegExpr, пытаясь найти соответствие для текущей позиции, захватывает все больше этих символов, пока не находит соответствие. На этом он завершает поиск, не пытаясь выяснить, нельзя ли, сдвинувшись вперед, найти лучший вариант. Впрочем, строго говоря, нет способа понять - что значит "лучший"

Вы можете также найти более детальные пояснения в разделе 'Синтаксис'.


Автор

     Andrey V. Sorokin,

     St-Petersburg,

     Russia,

     anso@mail.ru, anso@usa.net,

     http://anso.da.ru, http://anso.virtualave.net

Пожалуйста, прежде чем присылать мне сообщения об ошибках или какие-либо вопросы, касающиеся использования библиотеки, вначале скачайте последнюю версию с моей домашней странички и прочитайте ЧАВО!

Эта библиотека основана на библиотеке Henry Spencer.

Она была переведена с Си на Object Pascal, дополнена дополнительными возможностями и

снабжена объектным интерфейсом.

Многие доработки были предложены (а иногда и реализованы) пользователями TRegExpr (см.

благодарности ниже).

---------------------------------------------------------------

     Благодарности

---------------------------------------------------------------

•	  Guido Muehlwitz - нашел и уничтожил очень серьезную ошибку обработки строк

•	  Stephan Klimek - тестирование в CPPB, предложил многие доработки

•	  Steve Mudford - реализовал параметр Offset

•	  Martin Baur (www.mindpower.com) - перевод этого справочного файла на немецкий, полезные предложения

•	  Yury Finkel - реализация поддержки UniCode, нашел и исправил неприятный баг

•	  Ralf Junker - весьма педантично просмотрел код и реализовал ряд блестящих оптимизаций.

•	  Simeon Lilov - перевод этого справочного файла на болгарский язык

•	  Filip Jirsбk and Matthew Winter (wintermi@yahoo.com) - помощь в реализации non-greedy режима

•	  Kit Eason - предоставил массу примеров выражений для описания синтаксиса

•	  Juergen Schroth - поиск ошибок и полезные предложения

 И многим другим за неоценимую помощь в охоте на баги !

Автор с удовольствием примет помощь в переводе этого руководства на другие языки.