Как заменить простые URL ссылками? - PullRequest
425 голосов
/ 01 сентября 2008

Я использую приведенную ниже функцию для сопоставления URL-адресов внутри данного текста и замены их для ссылок HTML. Регулярное выражение работает отлично, но в настоящее время я заменяю только первое совпадение.

Как я могу заменить все URL? Наверное, мне следует использовать команду exec , но я не совсем понял, как это сделать.

function replaceURLWithHTMLLinks(text) {
    var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/i;
    return text.replace(exp,"<a href='$1'>$1</a>"); 
}

Ответы [ 24 ]

320 голосов
/ 21 февраля 2014

Во-первых, использование собственного регулярного выражения для анализа URL - это ужасная идея . Вы должны представить, что это достаточно распространенная проблема, которую кто-то написал, отладил и протестировал библиотеку для нее, согласно RFC . URI являются сложными - посмотрите код для анализа URL в Node.js и страницу Википедии о схемах URI .

Существует множество крайних случаев, когда речь идет о парсинге URL: международные доменные имена , фактические (.museum) и несуществующие (.etc) TLD, странная пунктуация, включая круглые скобки , пунктуация в конце URL, имена хостов IPV6 и т. Д.

Я смотрел на тонну из библиотек , и есть несколько полезных, несмотря на некоторые недостатки:

Библиотеки, которые я быстро дисквалифицировал для этой задачи:

Если вы настаиваете на регулярном выражении, наиболее полным является регулярное выражение URL-адреса из компонента , хотя оно будет ложно обнаруживать некоторые несуществующие двухбуквенные ДВУ, просматривая его.

284 голосов
/ 01 сентября 2008

Замена URL-адресов ссылками (Ответ на общую проблему)

Регулярное выражение в вопросе пропускает много крайних случаев. При обнаружении URL-адресов всегда лучше использовать специализированную библиотеку, которая обрабатывает международные доменные имена, новые TLD, такие как .museum, круглые скобки и другие знаки пунктуации внутри и в конце URL-адреса, и многие другие крайние случаи. См. Сообщение в блоге Джеффа Этвуда Проблема с URL-адресами для объяснения некоторых других проблем.

Лучшее резюме библиотек соответствия URL содержится в Ответ Дана Даскалеску +100
(по состоянию на февраль 2014 года)


«Сделать регулярное выражение заменяющим более одного совпадения» (Ответ на конкретную проблему)

Добавьте «g» в конец регулярного выражения, чтобы включить глобальное сопоставление:

/ig;

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

145 голосов
/ 08 октября 2010

Я сделал несколько небольших модификаций в коде Трэвиса (просто чтобы избежать ненужного переобъявления - но он отлично работает для моих нужд, так что отличная работа!):

function linkify(inputText) {
    var replacedText, replacePattern1, replacePattern2, replacePattern3;

    //URLs starting with http://, https://, or ftp://
    replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
    replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');

    //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
    replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
    replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');

    //Change email addresses to mailto:: links.
    replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
    replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');

    return replacedText;
}
67 голосов
/ 19 августа 2011

Сделаны некоторые оптимизации для кода Linkify() Трэвиса выше. Я также исправил ошибку, из-за которой адреса электронной почты с форматами субдоменов не совпадали (например, example@domain.co.uk).

Кроме того, я изменил реализацию на прототип класса String, чтобы элементы можно было сопоставлять следующим образом:

var text = 'address@example.com';
text.linkify();

'http://stackoverflow.com/'.linkify();

В любом случае, вот сценарий:

if(!String.linkify) {
    String.prototype.linkify = function() {

        // http://, https://, ftp://
        var urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;

        // www. sans http:// or https://
        var pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim;

        // Email addresses
        var emailAddressPattern = /[\w.]+@[a-zA-Z_-]+?(?:\.[a-zA-Z]{2,6})+/gim;

        return this
            .replace(urlPattern, '<a href="$&">$&</a>')
            .replace(pseudoUrlPattern, '$1<a href="http://$2">$2</a>')
            .replace(emailAddressPattern, '<a href="mailto:$&">$&</a>');
    };
}
23 голосов
/ 30 января 2010

Спасибо, это было очень полезно. Я также хотел что-то, что связывало бы вещи, которые выглядели бы как URL - в качестве основного требования, это связывало бы что-то вроде www.yahoo.com, даже если префикс протокола http: // отсутствовал. Так что в основном, если "www." присутствует, это свяжет это и предположит, что это http: //. Я также хотел, чтобы электронные письма превратились в ссылки mailto :. ПРИМЕР: www.yahoo.com будет преобразован в www.yahoo.com

Вот код, с которым я закончил (комбинация кода с этой страницы и других вещей, которые я нашел в Интернете, и других вещей, которые я сделал самостоятельно):

function Linkify(inputText) {
    //URLs starting with http://, https://, or ftp://
    var replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
    var replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');

    //URLs starting with www. (without // before it, or it'd re-link the ones done above)
    var replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
    var replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');

    //Change email addresses to mailto:: links
    var replacePattern3 = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;
    var replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');

    return replacedText
}

Во 2-й замене часть (^ | [^ /]) заменяет www.whever.com только в том случае, если перед ней еще нет префикса // - чтобы избежать двойной ссылки, если URL-адрес уже был связан в первой заменить. Кроме того, возможно, что www.whever.com может находиться в начале строки, что является первым условием «или» в этой части регулярного выражения.

Это может быть интегрировано как плагин jQuery, как проиллюстрировано Джесси Р - но я специально хотел, чтобы обычная функция не действовала на существующий элемент DOM, потому что я беру текст, который у меня есть, и затем добавляю его DOM, и я хочу, чтобы текст был «связан», прежде чем я добавлю его, поэтому я пропускаю текст через эту функцию. Прекрасно работает.

17 голосов
/ 08 мая 2012

Идентифицировать URL сложно, потому что они часто окружены знаками препинания и потому что пользователи часто не используют полную форму URL. Существует много функций JavaScript для замены URL гиперссылками, но мне не удалось найти такую ​​функцию, которая бы работала так же, как и фильтр urlize, в основанной на Python веб-среде Django. Поэтому я перенес функцию Django urlize на JavaScript:

https://github.com/ljosa/urlize.js

Пример:

urlize('Go to SO (stackoverflow.com) and ask. <grin>', 
       {nofollow: true, autoescape: true})
=> "Go to SO (<a href="http://stackoverflow.com" rel="nofollow">stackoverflow.com</a>) and ask. &lt;grin&gt;"

Второй аргумент, если он равен true, вызывает вставку rel="nofollow". Третий аргумент, если он истинный, экранирует символы, которые имеют особое значение в HTML. См. файл README .

10 голосов
/ 21 августа 2011

Я внес изменение в Roshambo String.linkify () в emailAddressPattern, чтобы распознать aaa.bbb. @ Ccc.ddd адреса

if(!String.linkify) {
    String.prototype.linkify = function() {

        // http://, https://, ftp://
        var urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;

        // www. sans http:// or https://
        var pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim;

        // Email addresses *** here I've changed the expression ***
        var emailAddressPattern = /(([a-zA-Z0-9_\-\.]+)@[a-zA-Z_]+?(?:\.[a-zA-Z]{2,6}))+/gim;

        return this
            .replace(urlPattern, '<a target="_blank" href="$&">$&</a>')
            .replace(pseudoUrlPattern, '$1<a target="_blank" href="http://$2">$2</a>')
            .replace(emailAddressPattern, '<a target="_blank" href="mailto:$1">$1</a>');
    };
}
7 голосов
/ 25 июня 2010

Лучший скрипт для этого: http://benalman.com/projects/javascript-linkify-process-lin/

6 голосов
/ 24 марта 2016

Я искал в Google что-нибудь новее и наткнулся на это:

$('p').each(function(){
   $(this).html( $(this).html().replace(/((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g, '<a href="$1">$1</a> ') );
});

демо: http://jsfiddle.net/kachibito/hEgvc/1/

Работает очень хорошо для обычных ссылок.

5 голосов
/ 22 ноября 2012

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

 function make_link(string) {
    var words = string.split(' '),
        ret = document.createDocumentFragment();
    for (var i = 0, l = words.length; i < l; i++) {
        if (words[i].match(/[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi)) {
            var elm = document.createElement('a');
            elm.href = words[i];
            elm.textContent = words[i];
            if (ret.childNodes.length > 0) {
                ret.lastChild.textContent += ' ';
            }
            ret.appendChild(elm);
        } else {
            if (ret.lastChild && ret.lastChild.nodeType === 3) {
                ret.lastChild.textContent += ' ' + words[i];
            } else {
                ret.appendChild(document.createTextNode(' ' + words[i]));
            }
        }
    }
    return ret;
}

Есть некоторые предостережения, а именно с более старой поддержкой IE и textContent.

здесь - это демо.

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