преобразовать url в ссылки из строки, кроме случаев, когда они находятся в атрибуте тега html

Я пытаюсь преобразовать из ввода текстового поля ($_POST['content']) все URL-адреса для ссылки.

$content = preg_replace('!(\s|^)((https?://)+[a-z0-9_./?=&-]+)!i', ' <a href="$2" target="_blank">$2</a> ', nl2br($_POST['content'])." ");
$content = preg_replace('!(\s|^)((www\.)+[a-z0-9_./?=&-]+)!i', '<a target="_blank" href="http://$2"  target="_blank">$2</a> ', $content." ");

Форматы целевых ссылок: www.hello.com или http(s)://(www).hello.com

Но это, похоже, нарушает любой iframe, изображение или подобное,

Как правильное регулярное выражение игнорирует URL-адреса в тегах HTML?

Примечание: я знаю, что мне нужны два выражения; один для определения отсутствия ссылок на протокол (например, www.hello.com, поэтому мне нужно добавить его), а другой для обнаружения URL-адресов с протоколом (поэтому не нужно добавлять).


person Toni Michel Caubet    schedule 21.09.2012    source источник
comment
Вы можете привести пример, который не работает? Из-за (\s|^) это будет соответствовать только в том случае, если у вас есть пробел или строки, начинающиеся перед URL-адресом. Но внутри iframe и img у вас должен быть ", не так ли?   -  person Martin Ender    schedule 26.09.2012
comment
Всегда ли размещаемый контент будет содержать HTML?   -  person Ja͢ck    schedule 27.09.2012
comment
@ Джек всегда должен его поддерживать.   -  person Toni Michel Caubet    schedule 27.09.2012


Ответы (4)


Ваш код в том виде, в каком он есть, не должен вызывать особых проблем с фреймами iframe и т. Д., Потому что там у вас обычно есть " перед URL-адресом, а не пробел, как того требует ваш шаблон.

Однако здесь другое решение. Это может не сработать на 100%, если у вас есть один < или > в комментариях HTML или что-то подобное. Но в любом другом случае он должен хорошо вас обслуживать (и я не знаю, является ли это проблемой для вас или нет). Он использует отрицательный просмотр вперед, чтобы убедиться, что нет закрытия > перед любым открытием < (потому что это означает, что вы находитесь внутри тега).

$content = preg_replace('$(\s|^)(https?://[a-z0-9_./?=&-]+)(?![^<>]*>)$i', ' <a href="$2" target="_blank">$2</a> ', $content." ");
$content = preg_replace('$(\s|^)(www\.[a-z0-9_./?=&-]+)(?![^<>]*>)$i', '<a target="_blank" href="http://$2"  target="_blank">$2</a> ', $content." ");

Если вы не знакомы с этой техникой, вот несколько подробностей.

(?!        # starts the lookahead assertion; now your pattern will only match, if this subpattern does not match
[^<>]      # any character that is neither < nor >; the > is not strictly necessary but might help for optimization
*          # arbitrary many of those characters (but in a row; so not a single < or > in between)
>          # the closing >
)          # ends the lookahead subpattern

Обратите внимание, что я изменил разделители регулярных выражений, потому что теперь я использую ! в регулярном выражении.

Если вам не нужен первый подшаблон (\s|^) для URL-адресов вне тегов, теперь вы можете удалить и его (и уменьшить переменные захвата в замене).

$content = preg_replace('$(https?://[a-z0-9_./?=&-]+)(?![^<>]*>)$i', ' <a href="$1" target="_blank">$1</a> ', $content." ");
$content = preg_replace('$(www\.[a-z0-9_./?=&-]+)(?![^<>]*>)$i', '<a target="_blank" href="http://$1"  target="_blank">$1</a> ', $content." ");

И, наконец ... вы не собираетесь заменять URL-адреса, которые содержат якоря в конце? Например. www.hello.com/index.html#section1? Если вы пропустили это случайно, добавьте # к разрешенным символам URL:

$content = preg_replace('$(https?://[a-z0-9_./?=&#-]+)(?![^<>]*>)$i', ' <a href="$1" target="_blank">$1</a> ', $content." ");
$content = preg_replace('$(www\.[a-z0-9_./?=&#-]+)(?![^<>]*>)$i', '<a target="_blank" href="http://$1"  target="_blank">$1</a> ', $content." ");

РЕДАКТИРОВАТЬ: А как насчет + и %? Есть также несколько других символов, которые могут появляться в URL без кодирования. См. это. КОНЕЦ ИЗМЕНЕНИЯ

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

Последняя мысль. Правильным решением было бы использовать парсер DOM. Затем вы можете просто применить регулярное выражение, которое у вас уже есть, только к текстовым узлам. Однако ваша забота о структуре HTML очень ограничена, и это делает вашу проблему снова регулярной (если у вас нет несогласованных '‹' или '>' в комментариях HTML или JavaScript или CSS на странице). Если у вас есть такие особые случаи, вам действительно стоит изучить парсер DOM. Ни одно из представленных здесь (пока) решений не будет в этом случае безопасным.

person Martin Ender    schedule 25.09.2012
comment
Это именно то, что мне нужно. Спасибо! Можно ли добавить + и% к такой строке, или им нужен / - person betaman; 25.10.2013
comment
@betaman Полагаю, вы имели в виду обратную косую черту? Если вы поместите их в класс символов, их не нужно экранировать, нет. Вне класса символов + нужно экранировать, а % - нет. - person Martin Ender; 25.10.2013
comment
Попробовав множество решений, это удалось. Поскольку я хочу сохранить существующий HTML без изменений и заменить только ссылки в тексте. И я узнал немного больше о регулярных выражениях. Спасибо, m.buettner! - person betaman; 26.10.2013
comment
Кажется, это работает. Пример этого кода можно найти на sandbox.onlinephpfunctions.com/code/ - person user1432181; 03.05.2021

  1. На мой взгляд, url - это все, что начинается с https?:// и заканчивается пробелом или концом строки (вертикальный пробел или так называемая новая строка).
  2. Из-за первой точки изображения, ссылки и т. Д. Не будут заменены, потому что все они начинаются с "или> (за исключением случаев, когда ссылка <a href=" http..."> начинается с пробела, но это недопустимый html).
  3. Модификатор /m указывает регулярному выражению соответствовать каждой строке (так, чтобы совпадение, описанное в первой точке, работало).
  4. Функцию nl2br() следует использовать после замены (из-за ссылок, которые начинаются в начале строки).
  5. Пробелы до и после добавляются только в том случае, если пробел изначально существует в $ content (см. $ 1 и $ 3 во втором параметре функции preg_replace ()).
  6. Это решение поддерживает доменные имена со специальными символами, например www.moški.si.

Вход:

INPUT

Код:

<?php

$content =
    preg_replace(
        '~(\s|^)(https?://.+?)(\s|$)~im', 
        '$1<a href="$2" target="_blank">$2</a>$3', 
        $content
    );
$content = 
    preg_replace(
        '~(\s|^)(www\..+?)(\s|$)~im', 
        '$1<a href="http://$2" target="_blank">$2</a>$3', 
        $content
    );
$content = nl2br($content);

Вывод:

Вывод

Редактировать:

Пример ссылок без префиксов https?:// + пример одиночного вызова preg_replace() (шаблоны и замены - массив):

$content = 
    preg_replace(
        array(
            '~(\s|^)(www\..+?)(\s|$)~im', 
            '~(\s|^)(https?://)(.+?)(\s|$)~im', 
        ),
        array(
            '$1http://$2$3', 
            '$1<a href="$2$3" target="_blank">$3</a>$4', 
        ),
        $content
    );
$content = nl2br($content);

введите описание изображения здесь

person Glavić    schedule 25.09.2012
comment
Чем больше голосов против, тем меньше у вас шансов получить эту награду, если она будет автоматически распределена. - person Tiberiu-Ionuț Stan; 01.10.2012
comment
Меня не волнует награда! Я забочусь о знаниях. Если мой ответ неверен, я хотел бы знать, ПОЧЕМУ. Разве это слишком много, чтобы просить противников? - person Glavić; 01.10.2012
comment
Я только что сказал вам, по какой причине люди, проголосовавшие против, могли проголосовать против. - person Tiberiu-Ionuț Stan; 01.10.2012
comment
Если это правда, я могу написать только это: OMG и LOL! Если это действительно причина, я больше никогда не буду отвечать на вопросы щедро. - person Glavić; 01.10.2012
comment
@glavic Я поддержал и ваш ответ, и m-buettner, но обратите внимание, что он ответил правильно до вас. Я проверил оба ваших ответа, и они оба работают, хотя ваш выглядит как меньшее (лучшее) регулярное выражение и не включает ограничительную часть a-z0-9, поскольку имена доменов теперь могут иметь гораздо больше символов и быть на разных языках - person Anthony Hatzopoulos; 01.10.2012
comment
@AnthonyHatzopoulos: Я поддержал его ответ, но дело не в этом. Я не люблю отрицательные голоса без резервной копии. Хотелось бы знать, где я ошибся ... Хороший момент! Имена доменов со специальными символами не будут выбираться в других примерах. Отредактировал ответ и добавил пункт 6.) Спасибо за отзыв и голосование ;-) p.s. даже SO не поддерживает доменные имена со специальными символами ;-) - person Glavić; 01.10.2012
comment
Здорово! Но как избежать попадания на www.sample ...? - person Robin Carlo Catacutan; 26.06.2014

Позвольте мне предложить что-то менее прямолинейное: разделите вводимый текст на части html и non-html, а затем обработайте части, отличные от html, с помощью вашего регулярного выражения, объединив текст обратно в одну часть. Что-л. как:

  <?php
  $chunks = preg_split('/(<.*>)/Ums', $_POST['content'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
  $result = '';
  foreach ($chunks as $chunk) {
    if (substr($chunk,0,1) != '<') {
      /* do your processing on $chunk */
    }
    $result .= $chunk;
  }

Некоторые дополнительные советы:

  1. постарайтесь сохранить исходный текст и выполнить преобразование при его отображении. Это позволит вам улучшить / исправить код рендеринга, если в будущем вы обнаружите новую проблему / идею.
  2. (https?: //) + не должно быть в скобках, и вам не нужен +, потому что он соответствует "https: // https: //some.com" - просто укажите https?: // [a-z0 -9_./?=&-pting+
  3. то же про (www.) + :)
person disjunction    schedule 21.09.2012

Раньше это делалось сотни раз. На этой странице либо m-buettner, либо главич работает нормально, хотя мне нравится более короткое выражение лица Гливича.

Вот хороший php-ресурс для этого: http://code.iamcal.com/php/lib_autolink/

Повторяется в Stackoverflow:

Достойная подробная статья: - http://buildinternet.com/2010/05/how-to-automatically-linkify-text-with-php-regular-expressions/

person Anthony Hatzopoulos    schedule 01.10.2012