2008-11-13 16 views
20

Tengo un montón de contenido enviado por el usuario. Es HTML y puede contener URL. Algunos de ellos ya serán <a> (si el usuario es bueno) pero a veces los usuarios son perezosos y escriben www.something.com o en el mejor de los casos http://www.something.com.Necesito una buena expresión regular para convertir URL a enlaces, pero dejo vínculos existentes solo

No puedo encontrar una expresión regular decente para capturar las URL, pero ignoro las que están inmediatamente a la derecha de una comilla doble o '>'. ¿Alguien tiene uno?

Respuesta

14

Jan Goyvaerts, creador de RegexBuddy, tiene written a response al blog de Jeff Atwood que aborda los problemas que tuvo Jeff y proporciona una buena solución.

\b(?:(?:https?|ftp|file)://|www\.|ftp\.)[-A-Z0-9+&@#/%=~_|$?!:,.]*[A-Z0-9+&@#/%=~_|$] 

Con el fin de hacer caso omiso de coincidencias que ocurren justo al lado de un "o>, se podría añadir (?<![">]) del inicio de la expresión regular, por lo que obtener

(?<![">])\b(?:(?:https?|ftp|file)://|www\.|ftp\.)[-A-Z0-9+&@#/%=~_|$?!:,.]*[A-Z0-9+&@#/%=~_|$] 

Esto corresponderá con direcciones completas (http://.. .) y las direcciones que comienzan con www o ftp - usted está fuera de suerte con direcciones como ars.userfriendly.org ...

0

enchufe desvergonzado:.. usted puede mirar aquí (regular expression replace a word by a link) en busca de inspiración

.

La pregunta solicitada para reemplazar algunas palabras con un cierto enlace, a menos que ya haya un enlace. Entonces, el problema que tienes es más o menos lo mismo.

Todo lo que necesita es una expresión regular que coincida con una URL (en lugar de la palabra). La suposición más simple sería la siguiente: una URL (opcionalmente) comienza con "http://", "ftp://" o "mailto:" y dura mientras no haya espacios en blanco, saltos de línea, corchetes de etiquetas o comillas).

Cuidado, largo regex por delante. Aplicar sin distinción de mayúsculas y minúsculas.

(href\s*=\s*['"]?)?((?:http://|ftp://|mailto:)?[^.,<>"'\s\r\n\t]+(?:\.(?![.<>"'\s\r\n])[^.,!<>"'\s\r\n\t]+)+) 

Ten cuidado - esto también coincidirá con direcciones URL que son técnicamente inválido, y que reconocerán things.formatted.like.this como una dirección URL. Depende de tus datos si es demasiado insensible. Puedo ajustar la expresión regular si tiene ejemplos donde devuelve falsos positivos.

La expresión regular generará dos grupos de coincidencias. El Grupo 2 contendrá la coincidencia, que probablemente sea una URL. El Grupo 1 contendrá una cadena vacía o 'href="'. Puede usarlo como un indicador de que esta coincidencia se produjo dentro de un parámetro href de un enlace existente y no tiene que tocar eso.

Una vez que confirme que esto hace lo correcto para usted la mayor parte del tiempo (con datos proporcionados por el usuario, nunca puede estar seguro), puede hacer el resto en dos pasos, como lo propuse en el otro pregunta:

  1. Hacer un vínculo en cada URL que hay (a menos hay algo en el grupo 1 partido!) Este se producen dobles anidados <a> etiquetas para las cosas que tienen un enlace ya.
  2. Scan para incorrectamente anidados <a> etiquetas, la eliminación de la más interna
0

Para saltar existente que se acaban de utilizar una mirada detrás - añadir (?<!href=") al comienzo de su expresión regular, por lo que sería algo como esto:

/(?<!href=")http://\S*/ 

Obviamente, esto no es una solución completa para la búsqueda de todos los tipos de URL, pero esto debería resolver su problema de jugar con los ya existentes.

10

me hizo una ligera modificación en la expresión regular contenida en la respuesta original:

(?<![.*">])\b(?:(?:https?|ftp|file)://|[a-z]\.)[-A-Z0-9+&#/%=~_|$?!:,.]*[A-Z0-9+&#/%=~_|$] 

que permite más subdominios, y también se ejecuta una comprobación más completa en las etiquetas. Para aplicar esto a preg de PHP reemplazar, puede utilizar:

$convertedText = preg_replace('@(?<![.*">])\b(?:(?:https?|ftp|file)://|[a-z]\.)[-A-Z0-9+&#/%=~_|$?!:,.]*[A-Z0-9+&#/%=~_|$]@i', '<a href="\0" target="_blank">\0</a>', $originalText); 

Nota, que elimina @ de la expresión regular, con el fin de utilizarlo como un delimitador para preg_replace. Es bastante raro que @ se use en una URL de todos modos.

Obviamente, se puede modificar el texto de reemplazo, y quitar target = "_ blank", o añadir rel = "nofollow", etc.

Espero que ayude.

+0

He añadido un = a la (? ]) al inicio para no romper link etiquetas de anclaje (no citados). Nice regex btw :) – Joel

+0

@Joel: ¿Estás seguro de que quieres que ese lookbehind signifique "Afirmar que es imposible hacer coincidir un punto, un asterisco, una comilla o un corchete de ángulo de cierre antes de la posición actual en la cadena"? –

11

Este hilo es viejo como las colinas, pero lo encontré mientras trabajaba en mi propio problema: es decir, convertir cualquier URL en enlaces, pero deja en paz a cualquiera que ya esté dentro de las etiquetas de anclaje. Después de un tiempo, esto es lo que ha hecho estallar hacia fuera:

(?!(?!.*?<a)[^<]*<\/a>)(?:(?:https?|ftp|file)://|www\.|ftp\.)[-A-Z0-9+&#/%=~_|$?!:,.]*[A-Z0-9+&#/%=~_|$] 

mediante la siguiente entrada:

http://www.google.com 
http://google.com 
www.google.com 

<p>http://www.google.com<p> 

this is a normal sentence. let's hope it's ok. 

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

Ésta es la salida de un preg_replace:

<a href="http://www.google.com" rel="nofollow">http://www.google.com</a> 
<a href="http://google.com" rel="nofollow">http://google.com</a> 
<a href="www.google.com" rel="nofollow">www.google.com</a> 

<p><a href="http://www.google.com" rel="nofollow">http://www.google.com</a><p> 

this is a normal sentence. let's hope it's ok. 

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

Sólo quería contribuir vuelta para salvar a alguien en algún momento.

+5

Esto funcionó para mí. Eres un campeón! Se agregó la bandera 'i' y esta es la resultante php: '$ text = preg_replace ('@ (?! (?!. *? ) (?: (?: Https? | Ftp | file): // | www \. | ftp \.) [- A-Z0-9 + & # /% = ~ _ | $?!:,.] * [A-Z0-9 + & # /% = ~ _ | $] @ i ',' \0 ', $ text); 'las otras soluciones anteriores no funcionaron para mí en todos los casos. – dtbaker

1
if (preg_match('/\b(?<!=")(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[A-Z0-9+&@#\/%=~_|](?!.*".*>)(?!.*<\/a>)/i', $subject)) { 
    # Successful match 
} else { 
    # Match attempt failed 
} 
Cuestiones relacionadas