Advanced Regex: интеллектуальное автоматическое обнаружение и замена URL-адресов тегами привязки - PullRequest
7 голосов
/ 05 мая 2010

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

Его регулярное выражение работает, но после обнаружения требуется дополнительный код.

Мне удалось написать регулярное выражениеэто делает все за один раз.Вот как это выглядит (я разбил его на отдельные строки, чтобы было более понятно, что он делает):

1   (?<outer>\()?
2   (?<scheme>http(?<secure>s)?://)?
3   (?<url>
4       (?(scheme)
5           (?:www\.)?
6           |
7           www\.
8       )
9       [a-z0-9]
10      (?(outer)
11          [-a-z0-9/+&@#/%?=~_()|!:,.;čšžćđ]+(?=\))
12          |
13          [-a-z0-9/+&@#/%?=~_()|!:,.;čšžćđ]+
14      )
15  )
16  (?<ending>(?(outer)\)))

Как вы можете видеть, я использую именованные группы захвата (используется позже)в Regex.Replace()), и я также включил некоторые локальные символы (čšžćđ), которые также позволяют анализировать наши локализованные URL-адреса.Вы можете легко опустить их, если хотите.

В любом случае.Вот что он делает (ссылаясь на номера строк):

  • 1 - определяет, начинается ли URL-адрес с открытых фигурных скобок (содержится внутри фигурных скобок), и сохраняет его во «внешней» именованной группе захвата
  • 3 - запускает анализ самого URL (сохранит его в именованной группе захвата «url»)
  • 4-8 - if утверждение, в котором говорится: если присутствовал «sheme», то www.part является необязательным, в противном случае обязательно, чтобы строка была ссылкой (поэтому это регулярное выражение обнаруживает все строки, начинающиеся с http или www)
  • 9 - первый символ после http:// или www. должен быть либобуква или цифра (это можно увеличить, если вы хотите охватить еще больше ссылок, но я решил этого не делать, потому что не могу вспомнить ссылку, которая начиналась бы с какого-то непонятного символа)
  • 10-14 - if оператор, который говорит: если присутствовали "внешние" (фигурные скобки), захватывать все до последних закрывающих фигурных скобок, в противном случае захватывать все
  • 15 - закрывать именованную группу захвата для URL
  • 16 - если присутствовали открытые скобки, захватите также закрывающие скобки и сохраните их в «конечной» именованной группе захвата

В первой и последней строке также содержалось \s*,так что пользователь может также написать открытые скобки и вставить пробел перед вставкой ссылки.

В любом случае.Мой код, который выполняет замену ссылок фактическими элементами HTML привязки, выглядит примерно так:

value = Regex.Replace(
    value,
    @"(?<outer>\()?(?<scheme>http(?<secure>s)?://)?(?<url>(?(scheme)(?:www\.)?|www\.)[a-z0-9](?(outer)[-a-z0-9/+&@#/%?=~_()|!:,.;čšžćđ]+(?=\))|[-a-z0-9/+&@#/%?=~_()|!:,.;čšžćđ]+))(?<ending>(?(outer)\)))",
    "${outer}<a href=\"http${secure}://${url}\">http${secure}://${url}</a>${ending}",
    RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);

Как вы можете видеть, я использую именованные группы захвата для замены ссылки тегом Anchor:

"${outer}<a href=\"http${secure}://${url}\">http${secure}://${url}</a>${ending}"

Я мог бы также пропустить часть http (s) в отображении привязки, чтобы сделать ссылки более дружелюбными, но сейчас я решил не делать этого.

Вопрос

Я хотел бымои ссылки также должны быть заменены сокращениями. Поэтому, когда пользователь копирует очень длинную ссылку (например, если он скопирует ссылку с карт Google, которая обычно генерирует длинные ссылки), я хотел бы сократить видимую часть якорятег.Ссылка будет работать, но видимая часть тега привязки будет сокращена до некоторого количества символов.С таким же успехом я могу добавить многоточие в конце (и сделать вещи еще более совершенными).

Поддерживает ли метод Regex.Replace() заменяющие нотации, чтобы я все еще мог использовать один вызов?Что-то похожее на метод string.Format(), когда вы хотите отформатировать значения в строковом формате (десятичные, даты и т. Д.).

Ответы [ 2 ]

1 голос
/ 05 мая 2010

Вы можете разделить ${url} на две группы захвата - urlhead с количеством символов, которое вы хотите отобразить, и urltail с остальными. Вот пример с 10 символами; это несколько упрощает удаление условия, последнее (?<ending>(?(outer)(?=\)))) должно позаботиться об этом - оно возвращает назад и захватывает последнее ) при необходимости:

(?<outer>(?<=\())?
(?<scheme>http(?<secure>s)?://)?
(?<url>
    (?(scheme)
        (?:www\.)?
        |
        www\.
    )
    [a-z0-9]
    [-a-z0-9/+&@#/%?=~_()|!:,.;čšžćđ]{1,10}
)
(?<urltail>[-a-z0-9/+&@#/%?=~_()|!:,.;čšžćđ]+)
(?<ending>(?(outer)(?=\))))

Обратите внимание, что я также изменил outer и ending, чтобы они выглядели как обходные пути, поэтому они не фиксируются и не заменяются. Строка замены в этом случае выглядит следующим образом:

<a href=\"http${secure}://${url}${urltail}\">http${secure}://${url}</a>
1 голос
/ 05 мая 2010

Вы должны будете использовать перегрузку Regex.Replace, которая использует MatchEvaluator, делегат, который создает для вас текст замены.

Смотрите здесь: http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.matchevaluator.aspx

Технически это возможно с помощью регулярных выражений, делая то, что предлагает Коби. Однако я не уверен, что хотел бы попросить кого-либо (включая вас самих через несколько месяцев) сохранить это регулярное выражение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...