Проблема с регулярным выражением при создании простых текстовых URL, которые можно нажимать - PullRequest
6 голосов
/ 12 января 2012

Мне нужен работающий код Regex в C #, который обнаруживает URL-адреса в виде простого текста (http / https / ftp / ftps) в строке и делает их кликабельными, помещая вокруг них тег привязки с тем же URL-адресом.Я уже создал шаблон Regex, и код прилагается ниже.

Однако, если во входной строке уже есть какой-либо кликабельный URL, тогда вышеуказанный код помещает поверх него еще один тег привязки.Например, существующая подстрока в приведенном ниже коде: string sContent: "ftp://www.abc.com '> ftp: //www.abc.com" имеет другой тег привязки над ним, когда кодниже запускается.Есть ли способ исправить это?

        string sContent = "ttt <a href='ftp://www.abc.com'>ftp://www.abc.com</a> abc ftp://www.abc.com abbbbb http://www.abc2.com";

        Regex regx = new Regex("(http|https|ftp|ftps)://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);

        MatchCollection mactches = regx.Matches(sContent);

        foreach (Match match in mactches)
        {
            sContent = sContent.Replace(match.Value, "<a href='" + match.Value + "'>" + match.Value + "</a>");
        }

Кроме того, я хочу, чтобы код Regex делал электронные письма кликабельными с помощью тега mailto.Я могу сделать это сам, но вышеупомянутая проблема двойного тега привязки также появится в нем.

Ответы [ 4 ]

5 голосов
/ 12 января 2012

Я заметил в вашем примере тестовую строку, что если дублирующая ссылка, например, ftp://www.abc.com находится в строке и уже связано, тогда результатом будет двойная привязка этой ссылки. Регулярное выражение, которое у вас уже есть и предоставлено @stema, будет работать, но вам нужно по-другому подходить к тому, как заменить совпадения в переменной sContent.

Следующий пример кода должен дать вам то, что вы хотите:

string sContent = "ttt <a href='ftp://www.abc.com'>ftp://www.abc.com</a> abc ftp://www.abc.com abbbbb http://www.abc2.com";

Regex regx = new Regex("(?<!(?:href='|<a[^>]*>))(http|https|ftp|ftps)://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);

MatchCollection matches = regx.Matches(sContent);

for (int i = matches.Count - 1; i >= 0 ; i--)
{
    string newURL = "<a href='" + matches[i].Value + "'>" + matches[i].Value + "</a>";

   sContent = sContent.Remove(matches[i].Index, matches[i].Length).Insert(matches[i].Index, newURL);
}
5 голосов
/ 12 января 2012

Попробуйте это

Regex regx = new Regex("(?<!(?:href='|>))(http|https|ftp|ftps)://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);

Это должно работать для вашего примера.

(?<!(?:href='|>)) является негативным взглядом сзади, это означает, что шаблон соответствует только в том случае, если ему не предшествуют "href = '" или ">".

См. Обзор на регулярные-выражения.info

и особенно отрицательное утверждение с нулевой шириной за msdn

Смотрите что-то похожее на Regexr . Мне пришлось убрать чередование с взгляда сзади, но .net должен справиться с этим.

Обновление

Чтобы убедиться, что есть также (возможно, возможные) случаи, например, "<p>ftp://www.def.com</p>", я улучшил регулярное выражение

Regex regx = new Regex("(?<!(?:href='|<a[^>]*>))(http|https|ftp|ftps)://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);

Lookbehind (?<!(?:href='|<a[^>]*>)) теперь проверяет, что нет ни "href = '", ни тега, начинающегося с "

Выход тестовой строки

ttt <a href='ftp://www.abc.com'>ftp://www.abc.com</a> abc <p>ftp://www.def.com</p> abbbbb http://www.ghi.com

с этим выражением

ttt <a href='ftp://www.abc.com'>ftp://www.abc.com</a> abc <p><a href='ftp://www.def.com'>ftp://www.def.com</a></p> abbbbb <a href='http://www.ghi.com'>http://www.ghi.com</a>
1 голос
/ 19 января 2012

Я знаю, что опоздал на эту вечеринку, но есть несколько проблем с регулярным выражением, которые существующие ответы не решают.Первый и самый раздражающий, это тот лес обратной косой черты.Если вы используете дословные строки C #, вам не нужно делать все это двойное экранирование.И вообще, во-первых, большинство обратных слешей не были нужны.

Во-вторых, есть бит: ([\\w+?\\.\\w+])+.Квадратные скобки образуют класс символов, и все внутри них рассматривается либо как буквенный символ, либо как сокращение класса, например \w.Но избавиться от квадратных скобок недостаточно, чтобы заставить его работать.Я подозреваю, что это то, что вы пытались: \w+(?:\.\w+)+.

В-третьих, квантификаторы в конце регулярного выражения - ]*)? - не совпадают.* может соответствовать нулю или более символов, поэтому нет смысла делать дополнительную группу включающей.Кроме того, такая компоновка может привести к серьезному снижению производительности.Подробнее см. на этой странице .

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

@"(?n)(https?|ftps?)://\w+(\.\w+)+([-a-zA-Z0-9~!@#$%^&*()_=+/?.:;',\\]*)(?![^<>]*+(>|</a>))"

Отрицательный прогноз - (?![^<>]*+(>|</a>)) - это то, что предотвращает совпадения внутри тегов или в содержимом элемента привязки.Это все еще очень грубо, хотя.Есть несколько областей, например, внутри <script> элементов, где вы не хотите, чтобы они совпадали, но это так.Но попытка охватить все возможности привела бы к регулярному выражению длиной в милю.

0 голосов
/ 22 февраля 2012

Checkout: Обнаружение электронной почты в тексте с использованием регулярных выражений и Regex URL. Замена, игнорирование изображений и существующих ссылок , просто замените регулярное выражение для ссылок, оно никогда не заменит ссылку внутритег, только в содержании.

http://html -agility-pack.net /? z = codeplex

Что-то вроде:


string textToBeLinkified = "... your text here ...";
const string regex = @"((www\.|(http|https|ftp|news|file)+\:\/\/)[_.a-z0-9-]+\.[a-z0-9\/_:@=.+?,##%&amp;~-]*[^.|\'|\# |!|\(|?|,| |>|<|;|\)])";
Regex urlExpression = new Regex(regex, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);

HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(textToBeLinkified);

var nodes = doc.DocumentNode.SelectNodes("//text()[not(ancestor::a)]") ?? new HtmlNodeCollection();
foreach (var node in nodes)
{
    node.InnerHtml = urlExpression.Replace(node.InnerHtml, @"<a href=""$0"">$0</a>");
}
string linkifiedText = doc.DocumentNode.OuterHtml;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...