Регулярные выражения Perl и их применение

       

Как укоротить длинные URL и длинные слова?


У вебмастеров иногда возникает задача укоротить длинные URL, которые вводят в форму участники форумов. Из-за длинных ссылок расползается дизайн страниц. Предположим, кто-то отослал на форум такой текст:

Рекомендую посетить: http://www.lsafhwoeufqfsjvdkghalhasdghasglhasgl.com/fasfasfasdf/

На самом деле ссылка может оказаться еще длинее. В HTML-коде страницы форума эта ссылка должна появиться в виде URL:

Рекомендую посетить: <a href="http://www.lsafhwoeufqfsjvdkghalhasdghasglhasgl.com/fasfasfasdf/" target="_blank"> http://www.lsafhwoeufqfsjvdkghalhasdghasglhasgl.com/fasfasfasdf/</a>

Внутри тега a ссылку, естественно, обрезать не надо, тем более, что в видимом тексте ее не будет, а вот то, что стоит перед тегом </a>, будет видно, и если это "слово" будет слишком длинным, то это может испортить дизайн. Надо после 50-го символа такой ссылки вставить три точки и получить такой текст:

Рекомендую посетить: <a href="http://www.lsafhwoeufqfsjvdkghalhasdghasglhasgl.com/fasfasfasdf/" target="_blank"> http://www.lsafhwoeufqfsjvdkghalhasdghasglhasgl.co…</a>

Другие длинные слова (не ссылки) надо также урезать. Мало ли что может ввести пользователь… Я нашел такое решение:

s/(\A|\s)(?!href=")(\S{50})\S+/$1$2.../g;

Предполагаем, что текст находится в переменной $_. Регулярное выражение начинает поиск от начала текста и каждого пробельного символа. Если после этого не стоит фрагмент href=, то проверяется, есть ли после этого "слово" длиной 50 символов, после которого стоит непробельный символ. Если да, то происходит замена всего, что найдено на $1 - пробельный символ перед длинным "словом", 50 символов с начала этого "слова" на $1, эти же 50 символов с начала длинного "слова", а последующие непробельные символы заменяются на три точки. Если бы в начале шаблона вместо (\A|\s) стояло просто (\s), то длинное "слово", стоящее в самом начале текста, не было бы укорочено.

Вот еще один вариант с использованием кода Perl в части замены:

s/((\S{50})\S+)/index($1,'href=') > -1 ? $1 : "$2..."/ge;


Ищем 50 непробельных символов, сразу за которыми есть хотя бы один непробельный символ. Эти 50 символов берем в переменную $2, а все длинное "слово" берем в переменную $1. В части замены проверяем, есть ли фрагмент текста href= в перемнной $1.

Если он есть, то заменяем найденное на $1, т.е. на себя, а если нет, то заменяем найденное на текст $2, за которым идут три точки.

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

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

$_=' <pppp>' x 13000; $_.='<table>a'; if (/[^\s<>](?![^<>]*>)/g) { print "1\n".pos; } else { print "0\n"; }

Вначале мы создаем длинную строку (больше 90000 символов) из тегов, в конец которой вставляем букву a вне тегов. В регулярном выражении мы ищем символ, отличный от пробельного и символов < и >, который не находится внутри тега. "Не находится внутри тега" означает, что сразу после этого символа не должно быть текста, соответствующего шаблону [^<>]*>. В случае нахождения такого символа программа печатает единицу и смещение этого символа от начала текста.

Вот другое решение, которое в отличие от первого настроено быстро пропускать все символы, которые нас не интересуют, т.е. пробельные и теги <> вместе с их содержимым:

/\A(?>(?:(?>\s*)<(?>[^>]*)>)*)\S/g

Оно кажется сложным из-за наличия трех атомарных группировок. Вот этот же шаблон без них:

/\A(?:\s*<[^>]*>)*\S/g

Внутри скобок мы пропускаем последовательности пробельных символов \s* и конструкции <[^>]*>. Обратите внимание, что между этими подшаблонами излишне ставить знак альтернативы |. Вначале стоит привязка \A, чтобы поиск не начался внутри тега.

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


Содержание раздела