- Регулярные выражения (regular expressions) — современная технология поиска текстовых фрагментов в электронных документах, соответствующих определенным образцам. Правила (rules) — это новое название регулярных выражений. Именно так они именуются в последней версиии языка Perl, признанного лидера по работе со строками.
Образец (pattern), задающий правило поиска, по-русски также иногда называют «шаблоном», «маской».
Регулярные выражения произвели прорыв в электронной обработке текста в конце XX века. Сейчас регулярные выражения используются многими текстовыми редакторами и утилитами для поиска и изменения текста на основе выбранных правил. Многие языки программирования уже поддерживают регулярные выражения для работы со строками. На моё удивление, в Delphi не оказалось встроенного модуля/компонента для работы с регулярными выражениями. Это существенное упущение разработчиков Delphi. Однако, как известно, свято место пусто не бывает! Итак, знакомьтесь, TRegExpr — класс для работы с регулярными выражениями в Delphi. Т.к. офф сайт уже не работает взять можно из http://code.google.com/p/dm-icq-remote- ... egExpr.pas (внешка).
- Символы
- Любой символ совпадает с самим собой (если только он не относится к метасимволам). Любая последовательность символов совпадает с такой же во входной строке. Чтобы представить метасимволы ., - [ ] и другие в регулярных выражениях без интерпретации, то есть, в качестве простых (не специальных) символов необходимо предварить их обратной косой чертой: \. Этот приём называется защитой метасимволов. Например, чтобы представить сам символ «точка» (просто точка, и ничего более), надо написать \. (обратная косая черта, а за ней - точка). Сам метасимвол \ тоже может быть защищен, то есть представлен как \\ (две обратных косых черты), и тогда интерпретатор регулярных выражений воспримет его как простой символ обратной косой черты \. Также можно использовать escape-последовательности, например
\t // табуляция
\n // новая строка
- Символьный класс — просто конечный набор символов. Он ограничивается квадратными скобками и содержит перечисление символов, которые можно вместо него подставить. Заменяется он всего на один символ, входящий в это перечисление. Примеры:
Можно использовать - (дефис) для указания диапазонов символов.[абвгде] // простое перечисление символов
[а-яА-Я] // все русские буквы
[0-9a-z] // цифры и строчная латиница
[^0-9] // все символы, кроме цифр
Если первый символ класса (идущий сразу после открывающейся квардратной скобки) — ^, то это означает символ, который отсутствует в данном перечислении. Фактически префикс ^ инвертирует список; вместо того, чтобы перечислять символы принадлежащие классу, мы перечисляем символы, не входящие в него.
Некоторые популярные символьные классы имеют короткую запись.
- Фиксирующие директивы — это символы, которые привязывают правило к некоторому признаку. Например, к концу или началу строки. Наиболее часто используемые:
^ // начало строки
$ // конец строки
- Показывают, сколько раз может повторяться предыдущий символ (символьный класс, альтернатива и т.д.) Ограничиваются парой фигурных скобок. Примеры:
\w{3} // три латинских буквы или цифры
\d{1, 3} // одна, две или три цифры
[а-яА-Я]{3, } // русское слово длинной три символа и больше
- С одним параметром называются точными и указывают точное количество повторений.
С двумя агрументами называются конечными и указывают конечный диапазон, в котором варьируется количество повторений.
Без второго параметра (но с запятой) называются бесконечными и ограничивают количество повторений лишь снизу.
Некоторые популярные имеют короткую запись.
- Речь пойдёт о жадности среди квантификаторов. квантификаторам в регулярных выражениях соответствует максимально длинная строка из возможных (они являются «жадными», greedy). Это может оказаться значительной проблемой. Например, часто ожидают, что выражение <.*> найдёт в тексте теги HTML. Однако этому выражению соответствует целиком строка
Эту проблему можно решить двумя способами. Первый состоит в том, что в регулярном выражении учитываются символы, не соответствующие желаемому образцу ([^>]* для вышеописанного случая). Второй заключается в определении квантификатора как "нежадного" ("ленивого", lazy), добавив после него знак вопроса.<p><b>Википедия</b> — свободная энциклопедия, в которой <i>каждый</i> может изменить или дополнить любую статью</p>
Например, выражению <.*?> соответствует не вся показанная выше строка, а отдельные теги (выделены цветом):
"Жадные" варианты квантификаторов пытаются захватить как можно большую часть входного текста, в то время как "не жадные" - как можно меньшую. Например, 'b+' как и 'b*' примененные к входной строке 'abbbbc' найдут 'bbbb', в то время как 'b+?' найдет только 'b', а 'b*?' - вообще - пустую строку; 'b{2,3}?' найдет 'bb', в то время как 'b{2,3}' найдет 'bbb'.<p><b>Википедия</b> — свободная энциклопедия, в которой <i>каждый</i> может изменить или дополнить любую статью</p>
- Нужны, когда необходимо объединить несколько правил в одно. При этом совпадение засчитывается, когда есть совпадение хотя бы с одним правилом. Желательно альтернативы заключать внутрь группировки (круглые скобки). Правила, входящие в вариант, разделяются | (вертикальной чертой). Примеры:
В данном примере продемонстрирована альтернатива в группировке. В принципе альтернатива может существовать и вне группировки, но так возникает больше ошибок.(жы|шы) // или "жы", или "шы"
([a-zA-Z]+|[а-яА-Я]+) // или слово на латинице, или русское
- Используются, когда необходимо обрабатывать результат частями. Например, при обработке ссылок в HTML-документе удобно отдельно обрабатывать текст ссылки и URL. Группировки заключаются в круглые скобки.
- Модификаторы предназначены для изменения поведения правила. Назначение и примеры - смотри в справке.
- Любой символ совпадает с самим собой (если только он не относится к метасимволам). Любая последовательность символов совпадает с такой же во входной строке. Чтобы представить метасимволы ., - [ ] и другие в регулярных выражениях без интерпретации, то есть, в качестве простых (не специальных) символов необходимо предварить их обратной косой чертой: \. Этот приём называется защитой метасимволов. Например, чтобы представить сам символ «точка» (просто точка, и ничего более), надо написать \. (обратная косая черта, а за ней - точка). Сам метасимвол \ тоже может быть защищен, то есть представлен как \\ (две обратных косых черты), и тогда интерпретатор регулярных выражений воспримет его как простой символ обратной косой черты \. Также можно использовать escape-последовательности, например
- Рассмотрим несколько примеров использования регулярных выражений в Delphi.
Пример использования- Использовать регулярные выражения в Delphi просто.
Распаковываем архив в любой каталог.
Добавляем RegExpr.pas (размещен в подкаталоге Source) в список файлов нашего проекта (главное меню Delphi Project -> Add to project...)
Используем класс TRegExpr в нашем проекте. Не забудьте добавить 'uses RegExpr' в соотв. модули проекта.
Итак, небольшой пример. Попробуем найти в строке все целые числа. Регулярное выражение для этого случая будет выглядеть так: -?\d+, т.е. содержать соответствующий символьный класс \d (т.е. цифры) и квантификатор +, означающий любое количество. Думаю, то, что целое число есть произвольное количество цифр понятно. ;-) Также перед числом возможно будет стоять знак «минус». Остальное должно быть понятно из коментариев.
Код: Выделить всё
// Очень простой пример - извлечение чисел из введённой строки. program Project1; {$APPTYPE CONSOLE} uses SysUtils, // добавляем нужный модуль RegExpr in 'RegExpr.pas'; var // нам необходим экземпляр класса TRegExpr RegExp: TRegExpr; s: string; begin // выводим запрос и считываем строку Write('Enter a string containing numbers: '); Readln(s); // создаём объект RegExp := TRegExpr.Create; // гарантирует освобождение занятой объектом памяти try // регулярное выражение находится в свойстве Expression RegExp.Expression := '-?\d+'; // ищем первое совпадение с помощью функции // Exec(const AInputString : string) : boolean, которая вернет true, // если в строке AInputString будет найдено совпадение c // регулярным выражением, хранящимся в свойтве Expression if RegExp.Exec(s) then // если находим begin Writeln('Entered string contains numbers: '); repeat // выводим найденное выражение, которое хранится в Match[0] Writeln(RegExp.Match[0]); // и продолжаем поиск until not RegExp.ExecNext; end else // иначе - сообщаем, что ничего не нашли Writeln('Entered string contains no numbers!'); finally RegExp.Free; end; Readln; end.
- Регулярное выражение для этого случая будет выглядеть несколько сложнее: (\d+)([.,])(\d+), т.е. содержать соотв. подвыражения (целая и дробная части) и вариант (в качестве разделителя может выступать как точка, так и запятая), который также представляет собой подвыражение.
Код: Выделить всё
// Чуть более сложный пример - извлечение целой и дробной части числа. // Регулярное выражение содержит варианты и подвыражения. program Project1; {$APPTYPE CONSOLE} uses SysUtils, // добавляем нужный модуль RegExpr in 'RegExpr.pas'; var // нам необходим экземпляр класса TRegExpr RegExp: TRegExpr; s: string; begin // выводим запрос и считываем строку Write('Enter a string containing numbers: '); Readln(s); // создаём объект RegExp := TRegExpr.Create; // гарантирует освобождение занятой объектом памяти try // регулярное выражение находится в свойстве Expression RegExp.Expression := '(\d+)([.,])(\d+)'; if RegExp.Exec(s) then // если находим begin Writeln('Whole number: ', RegExp.Match[0]); Writeln('Integer part: ', RegExp.Match[1]); Writeln('Divider : ', RegExp.Match[2]); Writeln('Fractional part: ', RegExp.Match[3]); end else // иначе - сообщаем, что ничего не нашли Writeln('Entered string doesn''t contain any number!'); finally RegExp.Free; end; Readln; end.
Выражение, соотв. всему регулярному выражению по-прежнему находится в Match[0], а вот Match содержит i-ую группировку.
Также обратите своё внимание на достаточно интересную функциюfunction Substitute (const ATemplate : string) : string;
которая возвращает ATemplate, в котором все '$&' и '$0' заменены на найденное регулярное выражение, а '$n' на n-ое подвыражение. Например, оператор
добавленный в предыдущий пример, вывел бы исходное число.Writeln(RegExp.Substitute('$1$2$3'));
Пример анализа текста- Давайте напишем простенький анализатор текста. На это раз сделаем GUI-приложение. На форме расположим один экземпляр TButton, один экземпляр TMemo и пять экземпляров TLabel.
Теперь давайте реализуем сбор статистики по мере изменения текста в Memo1:procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
end;Во-первых, остановимся на подсчёте количества символов, исключая пробельные. Здесь был использован методКод: Выделить всё
procedure TForm1.Memo1Change(Sender: TObject); var Count, i: Integer; RegExp: TRegExpr; begin RegExp := TRegExpr.Create; // кол-во строк Label1.Caption := 'Строк: ' + IntToStr(Memo1.Lines.Count); // кол-во символов Label2.Caption := 'Символов: ' + IntToStr(Length(Memo1.Text)); // кол-во символов, исключая пробельные Count := 0; RegExp.Expression := '\s'; Count := Count + Length(RegExp.Replace(Memo1.Text, '', False)); Label3.Caption := 'Непробельных символов: ' + IntToStr(Count); // кол-во слов Count := 0; RegExp.Expression := '\s*[^\s.-]+-?[^\s.-]*'; if RegExp.Exec(Memo1.Text) then repeat Count := Count + 1; until not RegExp.ExecNext; Label4.Caption := 'Слов: ' + IntToStr(Count); // кол-во предложений Count := 0; RegExp.Expression := '[.!?]+(\s|$)'; for i := 0 to Memo1.Lines.Count - 1 do if RegExp.Exec(Memo1.Text) then repeat Count := Count + 1; until not RegExp.ExecNext; Label5.Caption := 'Предложений: ' + IntToStr(Count); end;
заменяющий в AInputStr все вхождения регулярного выражения на AreplaceStr. Вычисляя сумму длин полученных таким образом строк, получим искомую величину.function Replace (AInputStr: RegExprString; const AReplaceStr: RegExprString; AUseSubstitution: boolean = False): RegExprString;
Теперь взглянем на подсчёт количества предложений в нашем примере. Ответом на вопрос «Что надо найти?», будет, скорее всего, «Количество точек, знаков восклицания и знаков вопроса, стоящих в конце слова или строки.» Кроме того, следует учитывать возможность наличия неединичных знаков препинания (…, !?). Составляем регулярное выражение '[.!?]+(\s|$)', и собственно всё! Задача решена!
При подсчёте количества слов можно, например, использовать регулярное выражение вида '\s*[^\s.-]+-?[^\s.-]*. Слово может следовать за пробелом, а может и нет (если стоит в начале текста). Поэтому вначале нашеговыражения стоит \s*. Собственно слово (написанное без орфографических ошибок) — последовательность непробельных символов. То есть мы могли бы записать регулярное вражение так: '\s*[\S]'. Но под такой шаблон попадут и некоторые другие символы и их последовательности, которые врядли можно назвать словами — многоточие, тире. Чтобы исправить ситуацию мы пишем '\s*[^\s.-]'. Ну и, наконец, необходимо учесть наличие слов, которые пишутся через дефис и получим '\s*[^\s.-]+-?[^\s.-]*.
При нажатии на кнопку, реализуем показ диалога выбора текстового файла и загрузку его в Memo1.
- Использовать регулярные выражения в Delphi просто.
Источник http://forum.vingrad.ru/articles/topic-213075.html