Intereting Posts
Как создать базу данных, если она не существует, используя PHP? php, как получить текущий десятичный / тысячный разделитель для данного языка php – simpleXML Как получить доступ к определенному элементу с тем же именем, что и другие? Запрос Mysql: получение запроса текущей даты Использование API телеграмм с PHP Полностью запретить кеширование браузера Как получить эскиз сообщения эскиза Laravel 5.3 Войти переадресации на разные страницы для нескольких пользователей Обновление JQuery Progressbar с ответом JSON в запросе ajax Морфинг от mysql до mysqli file_get_contents – не удалось открыть поток: HTTP-запрос не удался! HTTP / 1.1 404 не найден прерывистый «Попытка присвоить свойство не-объекта» по запросам / неспособность установить свойства объекта через $ this Войти и загрузить изображение в codeigniter 404 Не найдена ошибка при вызове web-сервисов clickbank Laravel: Неизвестный столбец 'updated_at'

Как заменить текстовые URL-адреса и исключать URL-адреса в тегах HTML?

Мне нужно, чтобы ты помог здесь.

Я хочу это сделать:

sometext sometext http://www.somedomain.com/index.html sometext sometext 

в:

 sometext sometext <a href="http://somedoamai.com/index.html">www.somedomain.com/index.html</a> sometext sometext 

Я справился с этим регулярным выражением:

 preg_replace("#((http|https|ftp)://(\S*?\.\S*?))(\s|\;|\)|\]|\[|\{|\}|,|\"|'|:|\<|$|\.\s)#ie", "'<a href=\"$1\" target=\"_blank\">$1</a>$4'", $text); 

Проблема в том, что он также заменяет URL-адрес img , например:

 sometext sometext <img src="http//domain.com/image.jpg"> sometext sometext 

превращается в:

 sometext sometext <img src="<a href="http//domain.com/image.jpg">domain.com/image.jpg</a>"> sometext sometext 

Пожалуйста помоги.

Оптимизированная версия Gumbo выше:

 $html = <<< HTML <html> <body> <p> This is a text with a <a href="http://example.com/1">link</a> and another <a href="http://example.com/2">http://example.com/2</a> and also another http://example.com with the latter being the only one that should be replaced. There is also images in this text, like <img src="http://example.com/foo"/> but these should not be replaced either. In fact, only URLs in text that is no a descendant of an anchor element should be converted to a link. </p> </body> </html> HTML; 

Давайте используем XPath, который извлекает только те элементы, которые на самом деле представляют собой текстовые файлы, содержащие http: // или https: // или ftp: //, и которые сами по себе не являются текстовыми элементами элементов привязки.

 $dom = new DOMDocument; $dom->loadHTML($html); $xPath = new DOMXPath($dom); $texts = $xPath->query( '/html/body//text()[ not(ancestor::a) and ( contains(.,"http://") or contains(.,"https://") or contains(.,"ftp://") )]' ); 

XPath выше даст нам TextNode со следующими данными:

  and also another http://example.com with the latter being the only one that should be replaced. There is also images in this text, like 

Начиная с PHP5.3 мы могли бы также использовать PHP внутри XPath для использования шаблона Regex для выбора наших узлов вместо трех входящих вызовов.

Вместо разделения текстовых разделов на соответствие стандарту, мы будем использовать фрагмент документа и просто заменить весь текстовый блок на фрагмент. Нестандартный в этом случае означает только метод, который мы будем использовать для этого , не является частью спецификации W3C API DOM .

 foreach ($texts as $text) { $fragment = $dom->createDocumentFragment(); $fragment->appendXML( preg_replace( "~((?:http|https|ftp)://(?:\S*?\.\S*?))(?=\s|\;|\)|\]|\[|\{|\}|,|\"|'|:|\<|$|\.\s)~i", '<a href="$1">$1</a>', $text->data ) ); $text->parentNode->replaceChild($fragment, $text); } echo $dom->saveXML($dom->documentElement); 

и это выдает:

 <html><body> <p> This is a text with a <a href="http://example.com/1">link</a> and another <a href="http://example.com/2">http://example.com/2</a> and also another <a href="http://example.com">http://example.com</a> with the latter being the only one that should be replaced. There is also images in this text, like <img src="http://example.com/foo"/> but these should not be replaced either. In fact, only URLs in text that is no a descendant of an anchor element should be converted to a link. </p> </body></html> 

Вам не следует делать это с помощью регулярных выражений – по крайней мере, не с регулярными выражениями. Вместо этого используйте правильный парсер HTML DOM, такой как библиотека DOM PHP . Затем вы можете перебирать узлы, проверять, является ли это текстовым узлом, и выполнять поиск по регулярному выражению и соответствующим образом заменять текстовый узел.

Что-то вроде этого должно это сделать:

 $pattern = "~((?:http|https|ftp)://(?:\S*?\.\S*?))(?=\s|\;|\)|\]|\[|\{|\}|,|\"|'|:|\<|$|\.\s)~i"; $doc = new DOMDocument(); $doc->loadHTML($str); // for every element in the document foreach ($doc->getElementsByTagName('*') as $elem) { // for every child node in each element foreach ($elem->childNodes as $node) { if ($node->nodeType === XML_TEXT_NODE) { // split the text content to get an array of 1+2*n elements for n URLs in it $parts = preg_split($pattern, $node->nodeValue, -1, PREG_SPLIT_DELIM_CAPTURE); $n = count($parts); if ($n > 1) { $parentNode = $node->parentNode; // insert for each pair of non-URL/URL parts one DOMText and DOMElement node before the original DOMText node for ($i=1; $i<$n; $i+=2) { $a = $doc->createElement('a'); $a->setAttribute('href', $parts[$i]); $a->setAttribute('target', '_blank'); $a->appendChild($doc->createTextNode($parts[$i])); $parentNode->insertBefore($doc->createTextNode($parts[$i-1]), $node); $parentNode->insertBefore($a, $node); } // insert the last part before the original DOMText node $parentNode->insertBefore($doc->createTextNode($parts[$i-1]), $node); // remove the original DOMText node $node->parentNode->removeChild($node); } } } } 

Хорошо, поскольку DOMNodeLists getElementsByTagName и childNodes живут , каждое изменение в DOM отражается в этом списке, и поэтому вы не можете использовать foreach , который также будет перебирать вновь добавленные узлы. Вместо этого вам нужно использовать вместо циклов и отслеживать добавленные элементы для увеличения указателей указателей и, в лучшем случае, предварительно рассчитанных границ массива.

Но так как это довольно сложно в таком сложном алгоритме (вам понадобится один указательный указатель и граница массива для каждого из трех циклов for циклов), использование рекурсивного алгоритма более удобно:

 function mapOntoTextNodes(DOMNode $node, $callback) { if ($node->nodeType === XML_TEXT_NODE) { return $callback($node); } for ($i=0, $n=count($node->childNodes); $i<$n; ++$i) { $nodesChanged = 0; switch ($node->childNodes->item($i)->nodeType) { case XML_ELEMENT_NODE: $nodesChanged = mapOntoTextNodes($node->childNodes->item($i), $callback); break; case XML_TEXT_NODE: $nodesChanged = $callback($node->childNodes->item($i)); break; } if ($nodesChanged !== 0) { $n += $nodesChanged; $i += $nodesChanged; } } } function foo(DOMText $node) { $pattern = "~((?:http|https|ftp)://(?:\S*?\.\S*?))(?=\s|\;|\)|\]|\[|\{|\}|,|\"|'|:|\<|$|\.\s)~i"; $parts = preg_split($pattern, $node->nodeValue, -1, PREG_SPLIT_DELIM_CAPTURE); $n = count($parts); if ($n > 1) { $parentNode = $node->parentNode; $doc = $node->ownerDocument; for ($i=1; $i<$n; $i+=2) { $a = $doc->createElement('a'); $a->setAttribute('href', $parts[$i]); $a->setAttribute('target', '_blank'); $a->appendChild($doc->createTextNode($parts[$i])); $parentNode->insertBefore($doc->createTextNode($parts[$i-1]), $node); $parentNode->insertBefore($a, $node); } $parentNode->insertBefore($doc->createTextNode($parts[$i-1]), $node); $parentNode->removeChild($node); } return $n-1; } $str = '<div>sometext http://www.somedomain.com/index.html sometext <img src="http//domain.com/image.jpg"> sometext sometext</div>'; $doc = new DOMDocument(); $doc->loadHTML($str); $elems = $doc->getElementsByTagName('body'); mapOntoTextNodes($elems->item(0), 'foo'); 

Здесь mapOntoTextNodes используется для сопоставления заданной функции обратного вызова на каждом узле DOMText в документе DOM. Вы можете либо передать весь узел DOMDocument, либо только определенный DOMNode (в данном случае только узел BODY ).

Затем функция foo используется для поиска и замены простых URL-адресов в содержимом узла DOMText путем разделения строки содержимого на части, отличные от URL / URL, с использованием preg_split при захвате используемого разделителя, в результате чего получается массив из 1 + 2 · n элементов , Затем части, не связанные с URL , заменяются новыми узлами DOMText, а части URL заменяются новыми элементами A , которые затем вставляются перед исходным узлом DOMText, который затем удаляется в конце. Поскольку это mapOntoTextNodes рекурсивно, достаточно просто вызвать эту функцию на определенном DOMNode .

спасибо за ответ, но он все еще работает. Я исправил эту функцию:

 function livelinked ($text){ preg_match_all("#((http|https|ftp)://(\S*?\.\S*?))(\s|\;|\)|\]|\[|\{|\}|,|\"|'|:|\<|$|\.\s)|^(jpg)#ie", $text, $ccs); foreach ($ccs[3] as $cc) { if (strpos($cc,"jpg")==false && strpos($cc,"gif")==false && strpos($cc,"png")==false ) { $old[] = "http://".$cc; $new[] = '<a href="http://'.$cc.'" target="_blank">'.$cc.'</a>'; } } return str_replace($old,$new,$text); } 

Если вы хотите продолжать использовать регулярное выражение (и в этом случае регулярное выражение вполне уместно), вы можете иметь регулярное выражение только для URL-адресов, которые «автономны». Используя escape-последовательность на границе слова ( \b ), вы можете иметь только соответствие регулярному выражению, где http сразу предшествует пробел или начало текста:

 preg_replace("#\b((http|https|ftp)://(\S*?\.\S*?))(\s|\;|\)|\]|\[|\{|\}|,|\"|'|:|\<|$|\.\s)#ie", "'<a href=\"$1\" target=\"_blank\">$1</a>$4'", $text); // ^^ thar she blows 

Таким образом, "http://..." не будет соответствовать, но http:// как его собственное слово.

DomDocument более зрелый и работает намного быстрее, поэтому это просто альтернатива, если кто-то хочет использовать PHP Simple HTML DOM Parser :

 <?php require_once('simple_html_dom.php'); $html = str_get_html('sometext sometext http://www.somedomain.com/index.html sometext sometext <a href="http://www.somedomain.com/index.html">http://www.somedomain.com/index.html</a> sometext sometext <img src="http//domain.com/image.jpg"> sometext sometext'); foreach ($html->find('text') as $element) { // you can add any tag into the array to exclude from replace if (!in_array($element->parent()->tag, array('a'))) $element->innertext = preg_replace("#((http|https|ftp)://(\S*?\.\S*?))(\s|\;|\)|\]|\[|\{|\}|,|\"|'|:|\<|$|\.\s)#ie", "'<a href=\"$1\" target=\"_blank\">$1</a>$4'", $element->innertext); } echo $html; 

Вы можете попробовать мой код из этого вопроса :

 echo preg_replace('/<a href="([^"]*)([^<\/]*)<\/a>/i', "$1", 'sometext sometext <img src="http//domain.com/image.jpg"> sometext sometext'); 

Если вы хотите повернуть некоторые другие теги – это достаточно просто:

 echo preg_replace('/<img src="([^"]*)([^\/><]*)>/i', "$1", 'sometext sometext <img src="http//domain.com/image.jpg"> sometext sometext'); 

сопоставить пробел (\ s) в начале и конце строки url, это обеспечит

 "http://url.com" 

не соответствует

 http://url.com 

сопоставляется;