суббота, 29 августа 2009 г.

Отчёт по Cofe'n'Cod: Введение в Регулярные Выражения

Сегодня прошла встреча в формате Cofe'n'Cod
Думаю будет неплохо если я опубликую затрагиваемую мной тему.

Тема: Введение в RegExp

Введение.

Регулярные выражения поддерживаются во многих языках программирования и являются по настоящему мощным инструментом работы с текстом. Я постараюсь рассказать про базовый "Словарь RegExp".
Понимание происходящего в RegExp открывает много новых возможностей для программиста и значительно ускоряет его работу.
Пока я не могу отвечать на такие вопросы как "Как?" и "Почему?" я попробую ответить на вопрос "Что?" возможно это и не интересно, но крепкий фундамент и ответ на вопрос "Что есть регулярные выражения" помогут в дальнейшем понимать суть происходящего.

Знакомство с Регулярными Выражениями

Ни для кого не будет секретом если я скажу что у RegExp есть 2 типа символов
  • Специальные символы (метасимволы)
  • Символы (литералы)
Существует бесплатная программа egrep для выполнения регулярных выражений в текстовом файле.
Для составления регулярных выражений можно воспользоваться сервисом http://myregexp.com/
Итак рассмотрим простые метасимволы которые мы будем использовать практически каждый раз.
Описывать регулярные выражения я буду в символах ‼(19)

Базовые метасимволы RegExp

Начало и конец строки

‼^‼ - означает начало строки (крышка, циркумфлекс)
‼$‼ - означает конец строки
Теперь мы можем составить простейший слог ‼^$‼ данное выражение будет искать строки у которых есть начало и сразу же идёт конец
Специфика данных символов заключается в том что они не привязаны к конкретному символу строки, а связанны именно с позицией.

Символьный класс

‼[]‼ - содержимое класса определяет список символов, и подразумевается связка "или" между символами, а не "и" как это происходит вне класса.
К примеру можно вести поиск слова "Антон" или "антон" используя символьный класс ‼[Аа]нтон‼
Причём класс не обязательно должен состоять из 2х символов, но в контексте выражения он будет означать именно один символ из множества.

Дополнительно стоит отметить что у Символьного класса существует свои Метасимволы. Символ "-" может записывать интервалы. Так [123456789] и [1-9] будут обозначать одно и тоже. Если же внутри класса символ "-" необходимо использовать в качестве литера его необходимо употреблять сразу после "[".

Любой Символьный класс можно инвертировать метасимволом "^", в начале класса. Так выражение ‼[^Аа]нтон‼ будет искать слово которое не начинается на символы "А" или "а" и заканчивается "нтон", корректным к примеру будет "Онтон" или "кнтон"
Инвертированный класс лучше всего воспринимать как сокращённую запись символьного класса включающего все возможные символы кроме перечисленных.

Один произвольный символ

Метасимвол "." означает совпадение с любым символом. Так если мы ищем конкретную дату но записанную в разных форматах 17-12-1985 или 17.12.1985 или 17/12/1985 то мы можем использовать выражение ‼17.12.1985‼ для поиска нужных дат. Тем ни менее стоит учитывать что в данную выборку попадут и такие фраз 1781221985 поскольку метасимвол "." обозначает любой символ

Конструкция выбора

Метасимвол "|" обозначает условие "или" и работает вне символьного класса, к конструкции выбора так же относится ещё 2 метасимвола "(" и ")" которые ограничивают область действия конструкции выбора так к примеру ‼А|антон‼ и ‼(А|а)нтон‼ будут иметь абсолютно разное значение в первом случае будет искаться символ "А" в любом месте или слово "антон" в любом месте даже внутри слова, во втором случае будет искаться слова "Антон" или "антон" в любом месте.

Конструкция повторения

Иногда случается необходимость поиска повторяющихся символов для этого служат следующие метасимволы
  • "?" - не более одного символа
  • "+" - не менее одного символа
  • "*" - любое количество символов даже их отсутствие
Так данные метасимволы можно использовать при поиски не обязательных пробелов, или наоборот обязательного символа.
Данные метасимволы можно использовать как на определённые символы так и на символьный класс
Также может быть доступна задание интервала повторений. Данный интервал указывается в "{х,у}" так интервал повторения {0, 1} соответствует "?"

Обратные ссылки

В регулярных выражениях можно использовать обратные ссылки. Данное свойство очень полезно для поиска повторяющихся символов в строке ‼([т]*)([о]).+\1\2‼
ссылка "\1" обращается к первой скобке, а "\2" ко второй, нумерация скобок идёт последовательно

Экранирование

Данная функция применяется при необходимости использовать метасимвол в качестве обычного литера, или придания литеру особого смысла. Символ используемый для экранирования "\" к примеру для поиска "." в тексте выражение типа ‼.‼ будет бесполезным поскольку данная фраза говорит что необходимо найти любой символ. В таком случае нам необходимо использовать экранирование ‼\.‼ тем самым мы говорим что необходимо искать именно точку.

Заключение

Вот собственно и перечислены основные "буквы" регулярных выражений. Осталось только научиться использовать их в нужных местах. Конечно есть множество особенностей в регулярных выражениях с которыми я ещё не успел познакомиться. Но надеюсь что фундамент для дальнейшего развития и понимания регулярных выражений заложен.
Для закрепления знаний предлагаю решить простую задачу, каждый сам для себя и обсудить пути её решения.
Также стоит отметить что регулярные выражения всегда ведут себя позитивно, т.е. они ищут присутствие символа, а не его отсутствие. Для полного понимания выражения стоит читать его буквально.

Задание для самоконтроля

Поиск номера телефона заданного формата в тексте, с игнорированием номеров указанных в ""
Здравствуйте уважаемые господа, для обратной связи со мной используйте телефон № 8(819)-238-91-28
Здравствуйте уважаемые господа, для обратной связи со мной используйте телефон № +7 819 238 91 28
Здравствуйте уважаемые господа, для обратной связи со мной используйте телефон № +8/819/238/91/28
Здравствуйте уважаемые господа, для обратной связи со мной используйте телефон № 8.(928)*233-91 28 с руско говорящим интерфейсом
Не звоните мне на номер "+7 147 233 87 92" поскольку он более не актуален.

3 комментария:

  1. Мои пути решения такие
    1. [^"]\+?[0-9].{0,2}[0-9]{3}.{0,2}[0-9]{3}.[0-9]{2}.[0-9]{2}[^"]
    2. [^"]\+?[0-9].{13,18}[0-9][^"]

    Как я рассуждал:
    1. Поскольку нам нужно исключить номер указанный в "" то говорим что первый символ может быть любым кроме ", затем идёт не обязательный 1 знак +, далее идёт код страны 1 цифра, потом может быть 2 или менее необязательных любых символа, потом 3 цифры, затем снова 2 или менее не обязательных символа, затем ещё 2 цифры, символ, ещё 2 цифры и всё это должно закончиться любым символом кроме как "
    2. выражение даёт менее точный результат, но для указанного задания полностью подходит
    Номер начинается с любого символа кроме ", за которым идёт не обязательный символ +, затем 1 цифра, далее от 13 до 18 любых символов, которые заканчиваются цифрой, и любым символом кроме ".

    ОтветитьУдалить
  2. Моё решение :)

    [^"](\+?[87].?[\( /][0-9]{3}[\) /][\-\*]?[0-9]{3}[\- /][0-9]{2}[\- /][0-9]{2})[^"]

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

    P.S. Если, что код страны может быть трехзначный, а 8 (без плюса) не является кодом страны.

    P.P.S Coffe'n'Code

    ОтветитьУдалить
  3. Т.е. Ты решил, перечислить всю последовательность символов, которые могут встретиться в номере?
    Да кстати интересный манёвр с любым не обязательным символом после кода страны.
    И ещё альтернатива [\-\*] была бы [-*] поскольку внутри символьного класса не требуется экранирование "*" (это обычный литерал) а что бы использовать "-" в качестве литерала её необходимо указывать сразу после "["
    В общем точность твоего выражения выше чем у меня :).
    P.S. ага про код страны согласен но хотелось использовать + как не обязательный символ :).
    P.P.S. тему сейчас исправлю. Спасибо за поправку.

    ОтветитьУдалить