| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Регулярные выражения в Delphi |
| Автор: kemiisto 24.5.2008, 23:11 | ||||||||||||||||||||||||
| Вторая редакция статьи. Исправлены ошибки, синтаксис регулярных выражений описан в более распространенных терминах. Огромное спасибо http://forum.vingrad.ru/index.php?showuser=777&nickname=source777 за помощь в подготовке статьи! Последнюю редакцию статьи всегда можно получить по http://sites.google.com/site/kemiisto/article/tregexpr. Введение Регулярные выражения (regular expressions) — современная технология поиска текстовых фрагментов в электронных документах, соответствующих определенным образцам. Правила (rules) — это новое название регулярных выражений. Именно так они именуются в последней версиии языка Perl, признанного лидера по работе со строками. Образец (pattern), задающий правило поиска, по-русски также иногда называют «шаблоном», «маской». Регулярные выражения произвели прорыв в электронной обработке текста в конце XX века. Сейчас регулярные выражения используются многими текстовыми редакторами и утилитами для поиска и изменения текста на основе выбранных правил. Многие языки программирования уже поддерживают регулярные выражения для работы со строками. На моё удивление, в Delphi не оказалось встроенного модуля/компонента для работы с регулярными выражениями. Это существенное упущение разработчиков Delphi. Однако, как известно, свято место пусто не бывает! Итак, знакомьтесь, TRegExpr — класс для работы с регулярными выражениями в Delphi. Берём с http://www.regexpstudio.com/ http://regexpstudio.com/Downloads/regexpr_RU.rar с модулем и примерами. Составные части регулярных выражений
Любой символ совпадает с самим собой (если только он не относится к метасимволам). Любая последовательность символов совпадает с такой же во входной строке. Чтобы представить метасимволы ., - [ ] и другие в регулярных выражениях без интерпретации, то есть, в качестве простых (не специальных) символов необходимо предварить их обратной косой чертой: \. Этот приём называется защитой метасимволов. Например, чтобы представить сам символ «точка» (просто точка, и ничего более), надо написать \. (обратная косая черта, а за ней - точка). Сам метасимвол \ тоже может быть защищен, то есть представлен как \\ (две обратных косых черты), и тогда интерпретатор регулярных выражений воспримет его как простой символ обратной косой черты \. Также можно использовать escape-последовательности, например:
Символьные классы Символьный класс — просто конечный набор символов. Он ограничивается квадратными скобками и содержит перечисление символов, которые можно вместо него подставить. Заменяется он всего на один символ, входящий в это перечисление. Примеры:
Стандартные символьные классы ![]() Фиксирующие директивы Фиксирующие директивы — это символы, которые привязывают правило к некоторому признаку. Например, к концу или началу строки. Наиболее часто используемые:
Квантификаторы Показывают, сколько раз может повторяться предыдущий символ (символьный класс, альтернатива и т.д.) Ограничиваются парой фигурных скобок. Примеры:
Квантификаторы
Короткие записи популярных квантификаторов ![]() Жадность Речь пойдёт о жадности среди квантификаторов. квантификаторам в регулярных выражениях соответствует максимально длинная строка из возможных (они являются «жадными», greedy). Это может оказаться значительной проблемой. Например, часто ожидают, что выражение <.*> найдёт в тексте теги HTML. Однако этому выражению соответствует целиком строка <p><b>Википедия</b> — свободная энциклопедия, в которой <i>каждый</i> может изменить или дополнить любую статью</p> Эту проблему можно решить двумя способами. Первый состоит в том, что в регулярном выражении учитываются символы, не соответствующие желаемому образцу ([^>]* для вышеописанного случая). Второй заключается в определении квантификатора как "нежадного" ("ленивого", lazy), добавив после него знак вопроса. Например, выражению <.*?> соответствует не вся показанная выше строка, а отдельные теги (выделены цветом): <p><b>Википедия</b> — свободная энциклопедия, в которой <i>каждый</i> может изменить или дополнить любую статью</p> "Жадные" варианты квантификаторов пытаются захватить как можно большую часть входного текста, в то время как "не жадные" - как можно меньшую. Например, 'b+' как и 'b*' примененные к входной строке 'abbbbc' найдут 'bbbb', в то время как 'b+?' найдет только 'b', а 'b*?' - вообще - пустую строку; 'b{2,3}?' найдет 'bb', в то время как 'b{2,3}' найдет 'bbb'. Альтернативы Нужны, когда необходимо объединить несколько правил в одно. При этом совпадение засчитывается, когда есть совпадение хотя бы с одним правилом. Желательно альтернативы заключать внутрь группировки (круглые скобки). Правила, входящие в вариант, разделяются | (вертикальной чертой). Примеры:
В данном примере продемонстрирована альтернатива в группировке. В принципе альтернатива может существовать и вне группировки, но так возникает больше ошибок. Группировки Используются, когда необходимо обрабатывать результат частями. Например, при обработке ссылок в HTML-документе удобно отдельно обрабатывать текст ссылки и URL. Группировки заключаются в круглые скобки. Модификаторы Модификаторы предназначены для изменения поведения правила. Назначение и примеры - смотри в справке. Использование в Delphi Рассмотрим несколько примеров использования регулярных выражений в Delphi. Пример использования Использовать регулярные выражения в Delphi просто.
Результат работы программы: ![]() Пример использования вариантов и подвыражений
Регулярное выражение для этого случая будет выглядеть несколько сложнее: (\d+)([.,])(\d+), т.е. содержать соотв. подвыражения (целая и дробная части) и вариант (в качестве разделителя может выступать как точка, так и запятая), который также представляет собой подвыражение. Выражение, соотв. всему регулярному выражению по-прежнему находится в Match[0], а вот Match[i] содержит i-ую группировку. Также обратите своё внимание на достаточно интересную функцию
которая возвращает ATemplate, в котором все '$&' и '$0' заменены на найденное регулярное выражение, а '$n' на n-ое подвыражение. Например, оператор
добавленный в предыдущий пример, вывел бы исходное число. Пример анализа текста Давайте напишем простенький анализатор текста. На это раз сделаем GUI-приложение. На форме расположим один экземпляр TButton, один экземпляр TMemo и пять экземпляров TLabel. ![]() При нажатии на кнопку, реализуем показ диалога выбора текстового файла и загрузку его в Memo1.
Теперь давайте реализуем сбор статистики по мере изменения текста в Memo1:
Во-первых, остановимся на подсчёте количества символов, исключая пробельные. Здесь был использован метод
заменяющий в AInputStr все вхождения регулярного выражения на AreplaceStr. Вычисляя сумму длин полученных таким образом строк, получим искомую величину. Теперь взглянем на подсчёт количества предложений в нашем примере. Ответом на вопрос «Что надо найти?», будет, скорее всего, «Количество точек, знаков восклицания и знаков вопроса, стоящих в конце слова или строки.» Кроме того, следует учитывать возможность наличия неединичных знаков препинания (…, !?). Составляем регулярное выражение '[.!?]+(\s|$)', и собственно всё! Задача решена! При подсчёте количества слов можно, например, использовать регулярное выражение вида '\s*[^\s.-]+-?[^\s.-]*. Слово может следовать за пробелом, а может и нет (если стоит в начале текста). Поэтому вначале нашеговыражения стоит \s*. Собственно слово (написанное без орфографических ошибок) — последовательность непробельных символов. То есть мы могли бы записать регулярное вражение так: '\s*[\S]'. Но под такой шаблон попадут и некоторые другие символы и их последовательности, которые врядли можно назвать словами — многоточие, тире. Чтобы исправить ситуацию мы пишем '\s*[^\s.-]'. Ну и, наконец, необходимо учесть наличие слов, которые пишутся через дефис и получим '\s*[^\s.-]+-?[^\s.-]*. Важно понимать, что основное преимущество при использовании регулярных выражений заключается в экономии времени! Вам необходимо лишь задаться вопросом «Что надо найти?» и составить регулярное выражение, которое является ответом на этот вопрос, а не разрабатывать «с нуля» алгоритм поиска. Успехов! http://kemiisto.yadviga.ru/articles/regexpr/samples.zip http://kemiisto.yadviga.ru/articles/regexpr/regexpr.pdf P.S. Для тестирования регулярных выражений можнов воспользоваться инструментом RegExp Studio, взять который можно http://www.regexpstudio.com/RegExpStudio.html. |
| Автор: kemiisto 25.5.2008, 11:03 |
MetalFan, согласен! Просто первое, что пришло на ум было '\.|\?|!' Спасибо! Рад стараться! |
| Автор: THandle 25.5.2008, 13:21 |
| kemiisto, хорошая статья, спасибо, начну изучать. Все понятно и хорошо написано |
| Автор: lukas 25.5.2008, 15:39 |
| Да регулярные выражение это крутая вещь... где я их только не использую... а на счет скорости работы функций из этого модуля... Все из них при обращении создают новый объект TRegExpr и исбавляются от него в конце, что и замедляет выполнение функции при большом кол-во использований... |
| Автор: Poseidon 25.5.2008, 17:12 |
| http://www.regexpstudio.com/RU/TRegExpr/Help/RegExp_Syntax.html |
| Автор: lukas 25.5.2008, 21:56 | ||
На этой странице проблемы с кодировкой, пробовал в 3х браузерах... скорее вот из за этого...
|
| Автор: Poseidon 25.5.2008, 23:24 |
| IE7 при установке в ручную "Кириллица (Windows)" нормально отображает |
| Автор: source777 29.5.2008, 10:37 |
Лучше уж заменить эту конструкцию на '[.!?](\s|$)', так будет намного точнее... Добавлено через 3 минуты и 42 секунды Вообще, я мельком только статью просмотрел, но даже при этом бросилось в глаза непонимание автором оператора | , очень во многих местах он используется абсолютно не к месту... |
| Автор: Foux 30.5.2008, 07:35 |
| Здесь рассмотрены примеры поиска конкретных символов, но как получить символы находящиеся между определенных символов, например содержимое HTML тэга? |
| Автор: source777 30.5.2008, 13:27 | ||
'<a\s+href=(.*?)\s.*?>(.*?)</a>' В итоге ссылка будет в RegExp.Match[1], а текст ссылки в RegExp.Match[2]. |
| Автор: kemiisto 25.9.2008, 02:05 | ||||||
source777, спасибо за feedback! Но, буду защищаться!
Хм... За прошедшее время пролистал несколько книг по регулярным выражениям. Вот что, к примеру, пишет Джеффри Фридл
Я и не путаю. Мне пришло в голову записать '\.|\?|!', Вам - '[.!?]'. Это фактически одно и тоже. Где же тут, простите, "непонимание"? Да и, вообще, "к месту/ не к месту" определяет, по-моему, автор.
Да, лучше. Но я же чётко написал: Добавлено через 2 минуты и 55 секунд Народ, а кто тег " Регульрные" добавил? Исправить можно? |
| Автор: source777 25.9.2008, 10:38 | ||
А во-вторых, это дополнение твоей заметки, а не перечёркивание её целиком, имхо, с твоей стороны вместо того, чтобы гордиться тем, что тебе пришло на ум по крайней мере, стоило бы внести изменения на более правильные варианты. |
| Автор: Matematik 25.9.2008, 12:29 |
| Еще есть TPerlRegEx http://www.regular-expressions.info/delphi.html В тяжелых случаях работает быстрее чем TRegExpr |
| Автор: Akella 25.9.2008, 17:51 | ||
Matematik,
|
| Автор: kemiisto 27.10.2008, 16:25 |
| Akella, имелось ввиду PCRE-based Components for Delphi for Windows/Win32 (чуть ниже, того что ты привёл). Будет время посмотрю как там работа организована. |
| Автор: WaReZMEN 13.1.2009, 17:44 |
| Статья супер все простые варианты я понял и активно заюзал но столкнулся со сложным вариантом помогите разобраться пожалуйста вот текст ABC := 'dsfdfdsf' Хочу получить только слово ABC ABC.ABC := 'dsfdfdsf' Ничего не получу потому как есть "." ABC :Integer; ничего не берем ну естественно пробелы до ABC куча может быть и после тоже... а также под ABC понимается некоторое слово на латинице.... |
| Автор: kemiisto 13.1.2009, 18:17 |
| WaReZMEN, может я что не так понял в постановке задачи, но не вижу ничего сложного. Регулярное выражение ([a-zA-Z]+) :=, нужное слово будет в RegExp.Match[1]. А может всё-таки не только слово, нои любой идентификатор? Уточните задачу. |
| Автор: WaReZMEN 13.1.2009, 19:55 |
| там может быть не только латинское слово также и цифры и прочая лабуда но интересует чтоб не было точки (SDF.ABC) и после не ABC должно быть := P.S. если строчка будет вида ABC := 'fdsfdsf'; FGHJ.DFR1 :='dfdgdfg'; то все равно берем ABC если б во второй части (FGHJ.DFR1 :='dfdgdfg') не было точки, а было бы DFR1 :='dfdgdfg' мы бы еще получили и DFR1. |
| Автор: source777 13.1.2009, 19:58 |
тогда уж, \b([a-zA-Z]+)\s*:= т.к. Кроме того, можно долго спорить о том, как может выглядеть слово на латинице, оно отнюдь не ограничено одними буквами, в случае идентификаторов всё проще, они однозначно детерминируются синтаксисом конкретного языка программирования. |
| Автор: WaReZMEN 13.1.2009, 20:06 |
| язык паскаль мне нужно из кода выделить только переменые проблема в том что они в var не объявлены а просто в коде попадаются типа как в байсике де хачу там пишу... И равно не нужно выносить... Добавлено через 9 минут и 19 секунд source777, если как ты говориш то при такой ABC := 'fdsfdsf'; FGHJ.DFR :='dfdgdfg'; строке получится и ABC и DFR а далжно получится только ABC потому что у DFR в начале стоит точка. Чуть не забыл если точка не слитно со словом то оно считается без точки (FGHJ. DFR :='dfdgdfg' мы получим DFR). |
| Автор: source777 13.1.2009, 20:46 |
| (\s|^)([a-zA-Z]+)\s*:=, нужное слово будет в RegExp.Match[2]. |
| Автор: WaReZMEN 13.1.2009, 21:01 |
| О спасибо!!! а можно расшифровать и пояснить почему RegExp.Match[2]? |
| Автор: source777 13.1.2009, 21:43 |
| Да, на здоровье. RegExp.Match[2] потому, что нужное тебе слово находится внутри второй пары круглых скобок в регулярке. А расшифровка примерно такая: (пробельный символ или начало строки)(один или более символов из набора латинских букв) любое_число_пробельных_символов := |
| Автор: WaReZMEN 15.1.2009, 17:44 |
| Снова к вам На этот раз хочу получить значение между скобками [ ] делаю так \[(.*)\] в результате получаю все но мне нужно те значения из скобок которые содержат a-zA-Z а также _ " . делаю так \[([a-zA-Z0-9_"\.]+)\] подскажите где ошибка? P.S. точек и "_" может быть от 1-~ а вот кавычки " только 2 Добавлено через 7 минут и 38 секунд Вот строка для теста Итого: [round([SUMM]*100)/100] [OrdGridsDM.IBtblOrders."ORD_CURRENCY"][txt] должно получится [OrdGridsDM.IBtblOrders."ORD_CURRENCY"] |
| Автор: source777 15.1.2009, 19:05 | ||||
| Давай-ка ты будешь яснее объяснять что нужно... А именно: 1) пример текста 2) твоё регулярное выражение 3) найденные им совпадения 4) совпадения, которые регэксп должен был бы найти Пример: 1)
2) \[([a-zA-Z0-9_".]+)\] 3) [Java] [_".NET"] 4) [Java] [_".NET"] Добавлено через 2 минуты и 18 секунд
Почему? Должно получится: [SUMM] [OrdGridsDM.IBtblOrders."ORD_CURRENCY"] [txt] либо ты плохо объяснил, что тебе нужно... Добавлено через 6 минут и 37 секунд Вообще по описанию похоже, что ищется такое выражение: \[([\w.]*("[\w.]+")?[\w.]*)\] |
| Автор: WaReZMEN 15.1.2009, 20:28 |
| Вы правы я не совсем точно сказал... в строке обязательно будут a-zA-Z " . а вот 0-9 и _ не обязательно но могут. |
| Автор: source777 16.1.2009, 13:22 |
| Тогда так: \[([\w.]*"[\w.]+"[\w.]*)\] Эта регулярка найдёт выражение в квадратных скобках, если оно содержит две и только две двойных кавычки, внутри и снаружи которых могут быть латинские буквы, цифры, точки и знаки подчёркивания, кроме того внутри двойных кавычек должен быть хотя бы 1 символ... P.S. Не отклоняйся от предложенного формата описания того, что нужно, поверь, он нагляднее любых слов. |
| Автор: WaReZMEN 16.1.2009, 13:31 | ||
source777, и снова спасибо!!! Я все думал как вы все это помните... а щас нашел редактор стало легче и понятнее спасибо еще раз огромное.
что то я не понял |
| Автор: source777 16.1.2009, 13:54 | ||
Это общий совет на будущее, когда спрашиваешь про регулярные выражения, описывай проблему в формате 1) пример текста 2) твоё регулярное выражение 3) найденные им совпадения 4) совпадения, которые регэксп должен был бы найти так тебя быстрее поймут, да и ответить правильно будет проще, т.к. появляется чёткий и однозначный критерий правильности: п.3 совпадает с п.4.
P.S. ты ещё рекурсивных регэкспов не встречал, вот там реальная жесть |
| Автор: WaReZMEN 16.1.2009, 15:58 |
| source777, спасибо! а про он-лайн не знал буду знать я просто клевый редактор нашел ну не совсем такой как хотелось бы, но давольнотаки хороший... |
| Автор: WaReZMEN 17.4.2009, 11:20 |
| Здрасти снова я есть строка 'Итого: [round([SUMM]*100)/100] [OrdGridsDM.IBtblOrders."ORD_CURRENCY"]'#$D#$A'[txt]'#$D#$A сеичас делаю так \[\w.*?\] получаю [round([SUMM] [OrdGridsDM.IBtblOrders."ORD_CURRENCY"] [txt] хочу получать [round([SUMM]*100)/100] [OrdGridsDM.IBtblOrders."ORD_CURRENCY"] [txt] Тоесть скажем так что если у нас открылось две [[ то результат нужно брать с второй ] скобки если их будет 3 то с трете... зарание спасибо!!!! |
| Автор: source777 17.4.2009, 12:09 |
| Читай про рекурсивные шаблоны для регулярных выражений. P.S. Я не в курсе есть ли их поддержка для Delphi. |
| Автор: WaReZMEN 17.4.2009, 12:20 |
| source777, спасибо за совет... |
| Автор: m6a6g6 17.11.2009, 17:51 |
| Так как сайт Андрея Сорокина уже не работает, то дам ссылки на аналогичные библиотеки. Проект http://www.regular-expressions.info/delphi.html. Он совместим с последними (Unicode) версиями Delphi, не требует сторонних библиотек и перейти на него с TRegExpr не так уж и сложно. Удачи Вам. |
| Автор: Akella 18.11.2009, 22:05 |
Дык Математик уже озвучивал этот сайт ;) |
| Автор: chip_and_dayl 23.6.2010, 19:16 |
| А есть свойство, которое отключает проверку по регистру!?! |
| Автор: chip_and_dayl 23.6.2010, 19:47 |
| Уже нашел |
| Автор: Plavozont 10.12.2010, 00:43 |
| PCRE - Perl Compatible Regular Expressions http://www.pcre.org/ Оригинальная система регулярных выражений родившаяся в языке Perl, самая "прямая" на сегодняшний день, просто добавь pcre3.dll ! Добавлено через 3 минуты и 8 секунд ... ой тут же 3 страницы форума... не заметил... |
| Автор: Akella 31.3.2011, 12:09 |
| http://forum.vingrad.ru/forum/topic-258533.html |
| Автор: MetalFan 1.4.2011, 17:56 |
| Кто нибудь знает, как заставить PrelRegEx (что уже встроены в D XE) понимать кирилицу в качестве букв для шаблонов типа \w? |
| Автор: vicerm 1.2.2012, 10:35 |
| Здравствуйте. Как можно подружить границу слова \b с русскими буквами? \b(а|в|у)(\s) не ищет вообще ничего. Без \b работает, только находит и там где не надо. \b(a|as|the|and)(\s) работает правильно. В общем нужно выражение для вставки неразрывного пробела после предлога. |