Урок 8. Использование ссылок назадВ предыдущем уроке были введены подвыражения как способ объединения символов в группы. Первичное назначение этого типа группировки состояло в том, чтобы управлять повторением соответствий с шаблоном (как демонстрировалось в предыдущем уроке). В этом уроке мы рассмотрим, другое важное использование подвыражений — работу со ссылками назад. Понятие ссылки назад
Лучше всего объяснить необходимость в ссылках назад на примере. В HTML-документах заголовочные теги (от Текст <BODY> <H1>Welcome to my Homepage</H1> Content is divided into two sections:<BR> <H2>ColdFusion</H2> Information about Macromedia ColdFusion. <H2>Wireless</H2> Information about Bluetooth, 802.11, and more. </BODY> Регулярное выражение <[hH]1>.*</[hH]1> Результат
<BODY>
<H1>Welcome to my Homepage</H1>
Content is divided into two sections:<BR>
<H2>ColdFusion</H2>
Information about Macromedia ColdFusion.
<H2>Wireless</H2>
Information about Bluetooth, 802.11, and more.
</BODY>
Шаблон Можно, конечно, было бы использовать простой диапазон вместо 1. Тогда получилось бы вот что: Текст <BODY> <H1>Welcome to my Homepage</H1> Content is divided into two sections:<BR> <H2>ColdFusion</H2> Information about Macromedia ColdFusion. <H2>Wireless</H2> Information about Bluetooth, 802.11, and more. </BODY> Регулярное выражение <[hH][1-6]>.*?</[hH][1-6]> Результат <BODY> <H1>Welcome to my Homepage</H1> Content is divided into two sections:<BR> <H2>ColdFusion</H2> Information about Macromedia ColdFusion. <H2>Wireless</H2> Information about Bluetooth, 802.11, and more. </BODY>
Это, кажется, сработало; Замечание
Обратите внимание, что здесь использовался ленивый квантор .*?, а не жадный .*. Как объяснялось в уроке 5, "Повторение совпадений", кванторы типа * являются жадными, и потому шаблон Успех? Не совсем. Рассмотрим следующий пример (используя тот же шаблон): Текст <BODY> <H1>Welcome to my Homepage</H1> Content is divided into two sections:<BR> <H2>ColdFusion</H2> Information about Macromedia ColdFusion. <H2>Wireless</H2> Information about Bluetooth, 802.11, and more. <H2>This is not valid HTML</H3> </BODY> Регулярное выражение <[hH][1-6]>.*?</[hH][1-6]> Результат <BODY> <H1>Welcome to my Homepage</H1> Content is divided into two sections:<BR> <H2>ColdFusion</H2> Information about Macromedia ColdFusion. <H2>Wireless</H2> Information about Bluetooth, 802.11, and more. <H2>This is not valid HTML</H3> </BODY>
Тег заголовка, начинающийся с Проблема состоит в том, что вторая часть совпадения (часть, соответствующая закрывающему тегу) не имеет никакой информации о первой части совпадения (части, соответствующей открывающему тегу). И именно здесь ссылки назад становятся очень полезными. Соответствие со ссылками назадМы еще вернемся к проблеме с заголовком. Пока же проанализируем более простой пример, который вообще не может быть решен без использования ссылок назад. Предположим, что в тексте нужно найти все повторяющиеся (подряд) слова (т.е. опечатки, где то же самое слово было по ошибке напечатано дважды). Очевидно, при поиске второго вхождения слова должно быть известно предыдущее слово. Ссылки назад позволяют в шаблоне регулярного выражения обратиться к предыдущим совпадениям (в данном случае к ранее найденному слову). Посмотрим, как это работает. Вот некоторый текст, содержащий три пары повторно напечатанных слов, все эти пары нужно найти: Текст This is a block of of text, several words here are are repeated, and and they should not be. Регулярное выражение [ ]+(\w+)[ ]+\1 Результат This is a block of of text, several words here are are repeated, and and they should not be.
Шаблон, очевидно, работал, но как он работал? Выражение ЗамечаниеТермин "ссылка назад" обозначает объект, который ссылается назад на предыдущее выражение.
Что же в точности означает ЗамечаниеСсылки назад подобны переменным. Теперь, когда вы знаете, как используются ссылки назад, давайте повторно рассмотрим пример с заголовками HTML-документа. Используя ссылки назад, можно создать шаблон, который найдет любой открывающий тег заголовка и соответствующий закрывающий тег (и игнорирует любой непарный тег). Вот пример: Текст <BODY> <H1>Welcome to my Homepage</H1> Content is divided into two sections:<BR> <H2>ColdFusion</H2> Information about Macromedia ColdFusion. <H2>Wireless</H2> Information about Bluetooth, 802.11, and more. <H2>This is not valid HTML</H3> </BODY> Регулярное выражение <[hH][1-6]>.*?</[hH]\1> Результат <BODY> <H1>Welcome to my Homepage</H1> Content is divided into two sections:<BR> <H2>ColdFusion</H2> Information about Macromedia ColdFusion. <H2>Wireless</H2> Information about Bluetooth, 802.11, and more. <H2>This is not valid HTML</H3> </BODY> Замечание
К сожалению, синтаксис ссылок назад очень зависит от реализации регулярных выражений. В JavaScript для обозначения ссылки назад служит символ \, а в операциях замены используется $), те же самые обозначения применяются в Macromedia ColdFusion и vi. В Perl используется $ (так что вместо \1 используется $1). В .NET реализация регулярных вы¬ражений возвращает объект, обладающий свойством по имени Groups {Группы), которое содержит пары, так что match.Groups[1] относится к первому совпадению в С#, a match.Groups(1) относится к тому же самому совпадению в Visual Basic .NET. В языке PHP эта информация возвращается в массиве, названном $matches, так что $matches[1] относится к первому соответствию (хотя это поведение может быть изменено с помощью флажков). В языках Java и Python возвращается объект с соответствиями, который содержит массив group.
Снова были найдены три совпадения: одна пара ЗамечаниеСсылки назад будут работать только в том случае, если выражение, на которое они ссылаются, является подвыражением (и потому заключено в скобки). ЗамечаниеОтсчет соответствий обычно начинается с 1. Во многих реализациях совпадение с 0 может использоваться для ссылки на все выражение. Замечание
На подвыражения ссылаются по их относительным позициям: Выполнение операций заменыКаждое регулярное выражение, использованное к настоящему времени в этой книге, применялось для поиска, иными словами, для определения местонахождения текста в большем блоке текста. Действительно, большинство шаблонов регулярных выражений используются для поиска текста. Но это не все, что могут делать регулярные выражения; регулярные выражения могут также выполнять мощные операции замены. Для простых замен текста регулярные выражения не нужны. Например, для замены всех вхождений СА на California, a MI — на Michigan регулярные выражения определенно не нужны. Хотя такие операции с регулярными выражениями вполне законны, у них нет никаких преимуществ, и на самом деле было бы проще использовать любые доступные обычные функции для манипуляций со строками. Однако если при операциях замены используются ссылки назад, применения регулярных выражений не избежать. Приведу пример, который уже предварительно обсуждался в уроке 5, "Повторение совпадений". Текст Hello, ben@forta.com is my email address. Регулярное выражение \w+[\w\.]*@[\w\.]+\.\w+ Результат
Hello, ben@forta.com is my email address.
Этот шаблон идентифицирует адреса электронной почты в тексте (см. урок 5, "Повторение совпадений").
Но что, если нужно сделать так, чтобы по любому адресу электронной почты, встречающемуся в тексте, можно было отправить письмо? В HTML-документе вы бы использовали тег Текст Hello, ben@forta.com is my email address. Регулярное выражение (\w+[\w\.]*@[\w\.]+\.\w+) Замена <A HREF="mailto:$1">$1</A> Результат Hello, <A HREF="mailto:ben@forta.com">ben@forta.com</A> is my email address.
В операциях замены используются два регулярных выражения: одно — для того, чтобы определить шаблон поиска, а второе — для того, чтобы указать, чем заменить найденный текст. Ссылки назад могут содержать шаблоны, так что подвыражение, найденное в первом шаблоне, может использоваться во втором шаблоне. ЗамечаниеКак уже отмечалось, обозначение ссылки назад зависит от используемой реализации. В JavaScript применяется знак $, а не \. В ColdFusion применяется \ и для операций поиска, и для операций замены. ЗамечаниеКак видно из этого примера, на подвыражение можно ссылаться несколько раз, для этого достаточно указать ссылку там, где необходимо.
Рассмотрим еще один пример. Информация о пользователях хранится в базе данных, причем номера телефона хранятся в формате Текст 313-555-1234 248-555-9999 810-555-9000 Регулярное выражение (\d{3})(-)(\d{3})(-)(\d{4}) Замена ($1) $3-$5 Результат (313) 555-1234 (248) 555-9999 (810) 555-9000
Здесь снова используются два шаблона регулярных выражений. Первый выглядит намного более сложным, чем он является на самом деле, так что давайте разберем его. Выражение ЗамечаниеПри переформатировании текста часто полезно разбить текст на большое количество небольших подвыражений, чтобы облегчить переформатирование. Замена регистраВ некоторых реализациях регулярных выражений можно выполнять преобразования с помощью метасимволов, перечисленных в табл. 8.1. Таблица 8.1. Метасимволы замены регистра
Чтобы изменить регистр следующего символа,
Приведем простой пример, в котором текст внутри пары заголовочных тегов уровня Текст <BODY> <H1>Welcome to my Homepage</H1> Content is divided into two sections:<BR> <H2>ColdFusion</H2> Information about Macromedia ColdFusion. <H2>Wireless</H2> Information about Bluetooth, 802.11, and more. <H2>This is not valid HTML</H3> </BODY> Регулярное выражение (<[hH]1>)(.*?)(</[hH]1>) Замена $1\U$2\E$3 Результат <BODY> <H1>WELCOME TO MY HOMEPAGE</H1> Content is divided into two sections:<BR> <H2>ColdFusion</H2> Information about Macromedia ColdFusion. <H2>Wireless</H2> Information about Bluetooth, 802.11, and more. <H2>This is not valid HTML</H3> </BODY>
Шаблон РезюмеПодвыражения используются для определения наборов символов или выражений. Кроме повторения совпадений (о чем говорилось в предыдущем уроке), подвыражения могут быть использованы для записи шаблонов. Этот тип ссылки называют ссылкой назад. К сожалению, синтаксис ссылок назад зависит от реализации. Ссылки назад важны при установлении соответствия с текстом и в операциях замены. |
||||||||||||||