¿Cómo reemplazo espacios en blanco con guiones bajos y viceversa?

221

Quiero reemplazar espacios en blanco con guiones bajos en una cadena para crear URL agradables. Así que por ejemplo:

"This should be connected" becomes "This_should_be_connected" 

Estoy usando Python con Django. ¿Se puede resolver esto usando expresiones regulares?

Lucas
fuente
1
¿Cómo se puede lograr esto en la plantilla django? ¿Hay alguna forma de eliminar espacios en blanco? ¿Hay alguna etiqueta / filtro incorporado para hacer esto? Nota: slugifyno da el resultado deseado.
user1144616

Respuestas:

374

No necesitas expresiones regulares. Python tiene un método de cadena incorporado que hace lo que necesita:

mystring.replace(" ", "_")
rogeriopvl
fuente
29
Esto no funciona con otros caracteres de espacio en blanco, como \ t o un espacio sin interrupción.
Roberto Bonvallet
12
Sí, está en lo correcto, pero a los fines de la pregunta formulada, no parece necesario tener en cuenta esos otros espacios.
rogeriopvl
1
¿Necesito importar algo para que esto funcione? Me sale el siguiente error: AttributeError: el objeto 'builtin_function_or_method' no tiene el atributo 'replace'
Ocasta Eshu
2
Probablemente la variable a la que llamó reemplazar no era un tipo de cadena.
Snigdha Batra
44
Esta respuesta puede ser confusa, mejor escríbala como mystring = mystring.replace ("", "_") ya que no altera directamente la cadena sino que devuelve una versión modificada.
Mehdi
79

Reemplazar espacios está bien, pero podría sugerir ir un poco más lejos para manejar otros caracteres hostiles a las URL como signos de interrogación, apóstrofes, signos de exclamación, etc.

También tenga en cuenta que el consenso general entre los expertos de SEO es que los guiones son preferibles a los guiones bajos en las URL.

import re

def urlify(s):

    # Remove all non-word characters (everything except numbers and letters)
    s = re.sub(r"[^\w\s]", '', s)

    # Replace all runs of whitespace with a single dash
    s = re.sub(r"\s+", '-', s)

    return s

# Prints: I-cant-get-no-satisfaction"
print(urlify("I can't get no satisfaction!"))
Tríptico
fuente
Esto es interesante. Definitivamente voy a utilizar este consejo.
Lucas
Recuerde urllib.quote () la salida de su urlify () - ¿y si s contiene algo que no es ascii?
zgoda
1
Esto es bueno, pero el primer RE con \ W también eliminará los espacios en blanco con el resultado de que el RE posterior no tiene nada que reemplazar ... Si desea reemplazar sus otros caracteres con '-' entre tokens, reemplace el primer RE con un espacio único como se indica, es decir, s = re.sub (r "\ W", '& nbsp', s) (esto puede ser un problema de formato shonky en StackOverflow: meta.stackexchange.com/questions/105507/… )
tiluki
2
@Triptych ¿Qué quieres decir? Golondrina africana o europea?
tiluki
1
Otro pequeño problema con esto es que elimina los guiones preexistentes en la url, de modo que si el usuario hubiera intentado limpiar la cadena de la url antes de cargarla para que sea así, se eliminaría a thisisclean. Entonces s = re.sub (r '[^ \ w \ s-]', '', s). Puede ir un paso más allá y eliminar los espacios en blanco iniciales y finales para que el nombre de archivo no termine o comience con un guión con s = re.sub (r '[^ \ w \ s-]', '', s) .strip ()
Intenex
42

Django tiene una función 'slugify' que hace esto, así como otras optimizaciones amigables con URL. Está oculto en el módulo de filtros predeterminados.

>>> from django.template.defaultfilters import slugify
>>> slugify("This should be connected")

this-should-be-connected

Este no es exactamente el resultado que solicitó, pero IMO es mejor para usar en URL.

Daniel Roseman
fuente
Esa es una opción interesante, pero es una cuestión de gustos o cuáles son los beneficios de usar guiones en lugar de guiones bajos. Acabo de notar que Stackoverflow usa guiones como usted sugiere. Pero digg.com, por ejemplo, usa guiones bajos.
Lucas
Esta es la opción preferida (AFAIK). Toma tu cadena, slugifícala, guárdala en un SlugField y úsala en get_absolute_url () de tu modelo. Puede encontrar ejemplos en la red fácilmente.
shanyu
3
La gente de @Lulu usa guiones porque, durante mucho tiempo, los motores de búsqueda trataron los guiones como separadores de palabras, por lo que sería más fácil encontrar búsquedas de varias palabras.
James Bennett
@Daniel Roseman, ¿puedo usar esto con variables dinámicas? ya que estoy obteniendo sitios web dinámicos como una cadena en un verdadero
efímero
Esta es la respuesta correcta. Necesita desinfectar sus URL.
kagronick
40

Esto tiene en cuenta los caracteres en blanco que no sean el espacio y creo que es más rápido que usar el remódulo:

url = "_".join( title.split() )
xOneca
fuente
44
Más importante aún, funcionará para cualquier carácter de espacio en blanco o grupo de caracteres de espacio en blanco.
dshepherd
Esta solución no maneja todos los caracteres de espacio en blanco. (p \x8f. ej. )
Lokal_Profil
Buena captura, @Lokal_Profil! La documentación no especifica qué caracteres de espacio en blanco se tienen en cuenta.
xOneca
1
Esta solución tampoco preservará los delimitadores de repetición, ya que split () no devuelve elementos vacíos cuando se usa el comportamiento predeterminado "dividir en espacios en blanco". Es decir, si la entrada es "hola, (6 espacios aquí) mundo", esto dará como resultado "hola, _mundo" como salida, en lugar de "hola, ______ mundo".
FliesLikeABrick
20

Usando el remódulo:

import re
re.sub('\s+', '_', "This should be connected") # This_should_be_connected
re.sub('\s+', '_', 'And     so\tshould this')  # And_so_should_this

A menos que tenga múltiples espacios u otras posibilidades de espacios en blanco como se mencionó anteriormente, es posible que desee utilizar string.replacecomo otros han sugerido.

Jarret Hardie
fuente
Gracias, esto era exactamente lo que estaba pidiendo. Pero estoy de acuerdo, el "string.replace" parece más adecuado para mi tarea.
Lucas
Qué diablos, quise votar a favor de esto, pero por alguna razón fue rechazado y ahora mi voto está bloqueado. Lo siento, Jarret.
Dave Liu
10

use el método de reemplazo de cadenas:

"this should be connected".replace(" ", "_")

"this_should_be_disconnected".replace("_", " ")

mdirolf
fuente
6

Sorprendentemente, esta biblioteca aún no se menciona

paquete de python llamado python-slugify, que hace un muy buen trabajo de slugify:

pip install python-slugify

Funciona así:

from slugify import slugify

txt = "This is a test ---"
r = slugify(txt)
self.assertEquals(r, "this-is-a-test")

txt = "This -- is a ## test ---"
r = slugify(txt)
self.assertEquals(r, "this-is-a-test")

txt = 'C\'est déjà l\'été.'
r = slugify(txt)
self.assertEquals(r, "cest-deja-lete")

txt = 'Nín hǎo. Wǒ shì zhōng guó rén'
r = slugify(txt)
self.assertEquals(r, "nin-hao-wo-shi-zhong-guo-ren")

txt = 'Компьютер'
r = slugify(txt)
self.assertEquals(r, "kompiuter")

txt = 'jaja---lol-méméméoo--a'
r = slugify(txt)
self.assertEquals(r, "jaja-lol-mememeoo-a") 
Yash
fuente
5

Estoy usando el siguiente código para mis URL amigables:

from unicodedata import normalize
from re import sub

def slugify(title):
    name = normalize('NFKD', title).encode('ascii', 'ignore').replace(' ', '-').lower()
    #remove `other` characters
    name = sub('[^a-zA-Z0-9_-]', '', name)
    #nomalize dashes
    name = sub('-+', '-', name)

    return name

Funciona bien con caracteres unicode también.

Armandas
fuente
1
¿Podría explicar en qué difiere esto de la función slugify incorporada de Django?
Andy Baker
4

Python tiene un método incorporado en cadenas llamado replace que se usa así:

string.replace(old, new)

Entonces usarías:

string.replace(" ", "_")

Tuve este problema hace un tiempo y escribí un código para reemplazar los caracteres en una cadena. Tengo que empezar a recordar comprobar la documentación de Python porque tienen funciones integradas para todo.


fuente
3

OP está usando Python, pero en JavaScript (algo a tener en cuenta ya que las sintaxis son similares.

// only replaces the first instance of ' ' with '_'
"one two three".replace(' ', '_'); 
=> "one_two three"

// replaces all instances of ' ' with '_'
"one two three".replace(/\s/g, '_');
=> "one_two_three"
twmulloy
fuente
3
mystring.replace (" ", "_")

si asigna este valor a cualquier variable, funcionará

s = mystring.replace (" ", "_")

por defecto mystring no tendrá esto

Rajesh
fuente
3

Puedes probar esto en su lugar:

mystring.replace(r' ','-')
Meghaa Yadav
fuente
-3
perl -e 'map { $on=$_; s/ /_/; rename($on, $_) or warn $!; } <*>;'

Match et replace space> guión bajo de todos los archivos en el directorio actual


fuente