¿Convertir una cadena en un nombre de archivo válido?
Tengo una cadena que quiero usar como nombre de archivo, por lo que quiero eliminar todos los caracteres que no estarían permitidos en los nombres de archivo, usando Python.
Prefiero ser estricto que lo contrario, así que digamos que quiero conservar solo letras, dígitos y un pequeño conjunto de otros caracteres como "_-.() "
. ¿Cuál es la solución más elegante?
El nombre del archivo debe ser válido en múltiples sistemas operativos (Windows, Linux y Mac OS); es un archivo MP3 en mi biblioteca con el título de la canción como nombre de archivo, y se comparte y se realiza una copia de seguridad entre 3 máquinas.
Puede consultar el marco de Django (¡pero tenga en cuenta su licencia!) para saber cómo crean un "slug" a partir de texto arbitrario. Un slug es compatible con URL y nombres de archivos.
Las utilidades de texto de Django definen una función, slugify()
que probablemente sea el estándar de oro para este tipo de cosas. Básicamente, su código es el siguiente.
import unicodedata
import re
def slugify(value, allow_unicode=False):
"""
Taken from https://github.com/django/django/blob/master/django/utils/text.py
Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated
dashes to single dashes. Remove characters that aren't alphanumerics,
underscores, or hyphens. Convert to lowercase. Also strip leading and
trailing whitespace, dashes, and underscores.
"""
value = str(value)
if allow_unicode:
value = unicodedata.normalize('NFKC', value)
else:
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
value = re.sub(r'[^\w\s-]', '', value.lower())
return re.sub(r'[-\s]+', '-', value).strip('-_')
Y la versión anterior:
def slugify(value):
"""
Normalizes string, converts to lowercase, removes non-alpha characters,
and converts spaces to hyphens.
"""
import unicodedata
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
value = unicode(re.sub('[-\s]+', '-', value))
# ...
return value
Hay más, pero lo omití, ya que no aborda la slugificación, sino el escape.
Puede utilizar la comprensión de listas junto con los métodos de cadena.
>>> s
'foo-bar#baz?qux@127/\\9]'
>>> "".join(x for x in s if x.isalnum())
'foobarbazqux1279'
¿Cuál es la razón para utilizar cadenas como nombres de archivos? Si la legibilidad humana no es un factor, elegiría el módulo base64 que puede producir cadenas seguras para el sistema de archivos. No será legible pero no tendrás que lidiar con colisiones y es reversible.
import base64
file_name_string = base64.urlsafe_b64encode(your_string)
Actualización : modificado según el comentario de Matthew.