2009-06-11 13 views
3

J'essaie de convertir des URL dans un morceau de texte en hyperliens - en utilisant des expressions régulières. J'ai réussi à y parvenir, mais le problème est quand il y a des liens déjà existants dans le texteregex pour transformer les URL en liens sans se tromper avec les liens existants dans le texte

si

bla bla blah www.google.com bla blah <a href="www.google.com">www.google.com</a> 

devrait aboutir à

bla bla blah <a href="http://www.google.com">www.google.com</a> bla blah <a href="www.google.com">www.google.com</a> 

pas

bla bla blah <a href="http://www.google.com">www.google.com</a> bla blah <a href="<a href="http://www.google.com">www.google.com</a></a>"><a href="http://www.google.com">www.google.com</a></a> 
+0

Avez-vous même essayé * * googler ce problème? Cela a été à travers ici tellement de fois que ce n'est même plus drôle (désolé si cela semble dédaigneux, c'est juste un fait). Regardez: http://www.google.com/search?q=url+links+regex+replace+site%3Astackoverflow.com – Tomalak

+1

Tomalak, lisez la question. Ce problème est plus compliqué que ce que vous trouvez avec cette recherche google – amarillion

+1

@amarillion: Bits et parties du problème ont été discutés ici sans fin. Même cette question exacte a été ici. Et chaque fois que cela se réduit à "ne pas faire du HTML avec regex", et "l'appariement d'URL dans un texte est difficile et impossible dans les cas de coin". Cette question sera sans aucun doute aussi brûlante. – Tomalak

Répondre

3

Enfin acheva:

function add_url_links($data) 
{ 
     $data = preg_replace_callback('/(<a href=.+?<\/a>)/','guard_url',$data); 

     $data = preg_replace_callback('/(http:\/\/.+?)([ \\n\\r])/','link_url',$data); 
     $data = preg_replace_callback('/^(http:\/\/.+?)/','link_url',$data); 
     $data = preg_replace_callback('/(http:\/\/.+?)$/','link_url',$data); 

     $data = preg_replace_callback('/{{([a-zA-Z0-9+=]+?)}}/','unguard_url',$data); 

     return $data; 
} 

function guard_url($arr) { return '{{'.base64_encode($arr[1]).'}}'; } 
function unguard_url($arr) { return base64_decode($arr[1]); } 
function link_url($arr) { return guard_url(array('','<a href="'.$arr[1].'">'.$arr[1].'</a>')).$arr[2]; } 
+0

Votre solution est innovante mais je pense que cela pourrait être beaucoup plus simple et plus rapide si votre langage regex a un look-behinds - ajoutez simplement' (? Nicole

3

Cette est presque impossible à faire avec une seule expression régulière. Je recommanderais plutôt une approche basée sur une machine d'état. Quelque chose comme ça (en pseudo-code)

state = OUTSIDE_LINK 
for pos (0 .. length input) 
    switch state 
    case OUTSIDE_LINK 
    if substring at pos matches /<a/ 
     state = INSIDE_LINK 
    else if substring at pos matches /(www.\S+|\S+.com|\S+.org)/ 
     substitute link 
    case INSIDE_LINK 
    if substring at post matches /<\/a>/ 
     state = OUTSIDE_LINK 
+1

@Tomalak - excuses, j'ai fait de mon mieux pour rechercher des questions similaires avant - et trouvé des messages similaires, mais aucun qui a répondu à ma question @amarillion Merci beaucoup, cela fonctionne. Je suis sûr qu'il doit y avoir un moyen de le faire en utilisant des rétrospectives négatives? Cependant, cette réponse est parfaite pour ce que j'essayais de faire. – Ben

2

Une autre façon de le faire (en php)

$strParts = preg_split('/(<[^>]+>)/', $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 
    foreach($strParts as $key=>$part) { 

     /*check this part isn't a tag or inside a link*/ 
     if(!(preg_match('@(<[^>]+>)@', $part) || preg_match('@(<a[^>]+>)@', $strParts[$key - 1]))) { 
      $strParts[$key] = preg_replace('@((http(s)?://)?(\S+\.{1}[^\s\,\.\!]+))@', '<a href="http$3://$4">$1</a>', $strParts[$key]); 
     } 

    } 
    $html = implode($strParts); 
+0

Votre code comporte une erreur 'Undefined offset: -1'. Le correctif consiste à changer 'preg_match ('@ (] +>) @', $ strParts [$ key - 1])' à 'preg_match ('@ (] +>) @', $ strParts [$ key? $ key - 1: 0]) ' –

1

Une autre astuce est de garder tous les liens existants en codant le code, puis en remplaçant urls avec liens, puis désencodage les liens protégés.

$data = 'test http://foo <a href="http://link">LINK</a> test'; 

$data = preg_replace_callback('/(<a href=".+?<\/a>)/','guard_url',$data); 

$data = preg_replace_callback('/(http:\/\/.+?)([ .\\n\\r])/','link_url',$data); 

$data = preg_replace_callback('/{{([a-zA-Z0-9+]+?)}}/','unguard_url',$data); 

print $data; 

function guard_url($arr) { return '{{'.base64_encode($arr[1]).'}}'; } 
function unguard_url($arr) { return base64_decode($arr[1]); } 
function link_url($arr) { return '<a href="'.$arr[1].'">'.$arr[1].'</a>'.$arr[2]; } 

Le code ci-dessus est juste une preuve de concept et ne gère pas toutes les situations. Pourtant, vous pouvez voir que le code est assez simple.

Questions connexes