Tengo una cadena que quiero usar como nombre de archivo, por lo que quiero eliminar todos los caracteres que no se permitirían en los nombres de archivo, usando Python.
Prefiero ser estricto que lo contrario, así que digamos que quiero retener solo letras, dígitos y un pequeño conjunto de otros caracteres como "_-.() "
. ¿Cuál es la solución más elegante?
El nombre de archivo debe ser válido en varios 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 realiza una copia de seguridad entre 3 máquinas.
os.path
realmente carga una biblioteca diferente según el sistema operativo (consulte la segunda nota en la documentación ). Entonces, si se implementó una función deos.path
cotización, solo podría citar la cadena para POSIX-safety cuando se ejecuta en un sistema POSIX o para windows-safety cuando se ejecuta en Windows. El nombre de archivo resultante no necesariamente sería válido tanto en Windows como en POSIX, que es lo que pide la pregunta.Respuestas:
Puede ver el marco de Django para ver cómo crean una "babosa" a partir de texto arbitrario. Una babosa es compatible con URL y nombre de archivo.
Las utilidades de texto de Django definen una función,
slugify()
que probablemente sea el estándar de oro para este tipo de cosas. Esencialmente, su código es el siguiente.Hay más, pero lo dejé fuera, ya que no aborda la slugificación, sino el escape.
fuente
value
. Si el valor debe ser Unicode, debe asegurarse de que realmente sea Unicode. O. Es posible que desee omitir la normalización Unicode si su valor real es en realidad una cadena ASCII.slugify
función se ha movido a django / utils / text.py , y ese archivo también contiene unaget_valid_filename
función.Este enfoque de la lista blanca (es decir, permitir solo los caracteres presentes en valid_chars) funcionará si no hay límites en el formato de los archivos o la combinación de caracteres válidos que son ilegales (como ".."), por ejemplo, lo que usted dice permitiría un nombre de archivo llamado ". txt" que creo que no es válido en Windows. Como este es el enfoque más simple, trataría de eliminar los espacios en blanco de los valid_chars y anteponer una cadena válida conocida en caso de error, cualquier otro enfoque tendrá que saber qué está permitido dónde hacer frente a las limitaciones de nomenclatura de archivos de Windows y así ser Mucho más complejo.
fuente
valid_chars = frozenset(valid_chars)
no dolería Es 1.5 veces más rápido si se aplica a todos los caracteres."CON"
en Windows te meterá en problemas ...Puede usar la comprensión de listas junto con los métodos de cadena.
fuente
filename = "".join(i for i in s if i not in "\/:*?<>|")
"".join( x for x in s if (x.isalnum() or x in "._- "))
¿Cuál es la razón para usar las cadenas como nombres de archivo? Si la legibilidad humana no es un factor, optaría por el módulo base64 que puede producir cadenas seguras del sistema de archivos. No será legible, pero no tendrá que lidiar con colisiones y es reversible.
Actualización : modificado según el comentario de Matthew.
fuente
your_string
debe ser una matriz de bytes o el resultado deencode('ascii')
que esto funcione.def url2filename(url): url = url.encode('UTF-8') return base64.urlsafe_b64encode(url).decode('UTF-8') def filename2url(f): return base64.urlsafe_b64decode(f).decode('UTF-8')
Para complicar aún más las cosas, no se garantiza que obtenga un nombre de archivo válido simplemente eliminando caracteres no válidos. Dado que los caracteres permitidos difieren en diferentes nombres de archivo, un enfoque conservador podría terminar convirtiendo un nombre válido en uno inválido. Es posible que desee agregar un manejo especial para los casos donde:
La cadena son todos caracteres no válidos (dejándolo con una cadena vacía)
Terminas con una cadena con un significado especial, por ejemplo, "." o ".."
En Windows, ciertos nombres de dispositivos están reservados. Por ejemplo, no puede crear un archivo llamado "nul", "nul.txt" (o nul.anything de hecho) Los nombres reservados son:
CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8 y LPT9
Probablemente pueda solucionar estos problemas anteponiendo alguna cadena a los nombres de archivo que nunca pueden dar lugar a uno de estos casos y eliminando caracteres no válidos.
fuente
Hay un buen proyecto en Github llamado python-slugify :
Instalar en pc:
Luego usa:
fuente
test.txt
ponetest-txt
que es demasiado.Al igual que S.Lott respondió, puede ver el Marco de Django para ver cómo convierten una cadena en un nombre de archivo válido.
La versión más reciente y actualizada se encuentra en utils / text.py, y define "get_valid_filename", que es el siguiente:
(Ver https://github.com/django/django/blob/master/django/utils/text.py )
fuente
django.utils.text import get_valid_filename
re.sub(r'(?u)[^-\w.]', '', s)
elimina todos los caracteres que no sean letras, ni números (0-9), ni el guión bajo ('_'), ni el guión ('-'), ni el punto ('.' ) "Letras" aquí incluye todas las letras unicode, como 漢語.Esta es la solución que finalmente utilicé:
La llamada unicodedata.normalize reemplaza los caracteres acentuados con el equivalente sin acento, lo cual es mejor que simplemente eliminarlos. Después de eso, se eliminan todos los caracteres no permitidos.
Mi solución no antepone una cadena conocida para evitar posibles nombres de archivo no permitidos, porque sé que no pueden aparecer dado mi formato de nombre de archivo particular. Una solución más general necesitaría hacerlo.
fuente
Tenga en cuenta que en realidad no hay restricciones para los nombres de archivos en sistemas Unix que no sean
Todo lo demás es juego limpio.
Sí, acabo de almacenar códigos de color ANSI en un nombre de archivo y los hice surtir efecto.
Para entretenerse, coloque un carácter BEL en el nombre de un directorio y vea la diversión que se produce cuando lo graba en CD;)
fuente
En una linea:
También puede poner el carácter '_' para hacerlo más legible (por ejemplo, en caso de reemplazar barras)
fuente
Puede usar el método re.sub () para reemplazar cualquier cosa que no sea "similar a un archivo". Pero en efecto, cada personaje podría ser válido; así que no hay funciones preconstruidas (creo), para hacerlo.
Resultaría en un identificador de archivo para /tmp/filename.txt.
fuente
No maneja cadenas vacías, nombres de archivo especiales ('nul', 'con', etc.).
fuente
Aunque tienes que tener cuidado. No se dice claramente en su introducción, si solo está mirando el lenguaje latine. Algunas palabras pueden dejar de tener sentido u otro significado si las desinfecta con caracteres ascii solamente.
imagina que tienes "forêt poésie" (poesía forestal), tu desinfección podría dar "fort-posie" (fuerte + algo sin sentido)
Peor si tienes que lidiar con caracteres chinos.
"下 北 沢" su sistema podría terminar haciendo "---" que está condenado a fallar después de un tiempo y no es muy útil. Por lo tanto, si maneja solo archivos, le recomendaría que los llame una cadena genérica que usted controla o que mantenga los caracteres como están. Para los URI, casi lo mismo.
fuente
¿Por qué no simplemente envolver el "osopen" con un try / except y dejar que el sistema operativo subyacente sepa si el archivo es válido?
Esto parece mucho menos trabajo y es válido sin importar qué sistema operativo use.
fuente
osopen
máquina en ejecución.Otro problema que los otros comentarios aún no han abordado es la cadena vacía, que obviamente no es un nombre de archivo válido. También puede terminar con una cadena vacía al eliminar demasiados caracteres.
¿Qué pasa con los nombres de archivos reservados de Windows y los problemas con los puntos, la respuesta más segura a la pregunta "¿cómo normalizo un nombre de archivo válido a partir de la entrada arbitraria del usuario?" es "no te molestes en intentarlo": si puedes encontrar alguna otra forma de evitarlo (por ejemplo, usando claves primarias enteras de una base de datos como nombres de archivo), hazlo.
Si debe hacerlo, y realmente necesita permitir espacios y '.' para extensiones de archivo como parte del nombre, intente algo como:
Incluso esto no se puede garantizar correctamente, especialmente en sistemas operativos inesperados, por ejemplo, el sistema operativo RISC odia los espacios y usa ''. como un separador de directorio
fuente
Me gustó el enfoque de pitón-slugify aquí, pero también estaba quitando puntos que no era deseable. Así que lo optimicé para subir un nombre de archivo limpio a s3 de esta manera:
Código de ejemplo:
Salida:
Esto es tan seguro, funciona con nombres de archivo sin extensión e incluso funciona solo con nombres de archivos de caracteres inseguros (el resultado está
none
aquí).fuente
Respuesta modificada para python 3.6
fuente
Me doy cuenta de que hay muchas respuestas, pero en su mayoría se basan en expresiones regulares o módulos externos, por lo que me gustaría agregar mi propia respuesta. Una función de Python pura, no se necesita un módulo externo, no se utiliza ninguna expresión regular. Mi enfoque no es limpiar los caracteres inválidos, sino solo permitir los válidos.
si lo desea, puede agregar sus propios caracteres válidos a la
validchars
variable al principio, como sus letras nacionales que no existen en el alfabeto inglés. Esto es algo que puede querer o no: algunos sistemas de archivos que no se ejecutan en UTF-8 podrían tener problemas con caracteres no ASCII.Esta función es para probar la validez de un solo nombre de archivo, por lo que reemplazará los separadores de ruta con _ considerándolos caracteres no válidos. Si desea agregar eso, es trivial modificar el
if
separador de ruta para incluir OS.fuente
La mayoría de estas soluciones no funcionan.
'/ hello / world' -> 'helloworld'
'/ helloworld' / -> 'helloworld'
En general, esto no es lo que desea, digamos que está guardando el html para cada enlace, va a sobrescribir el html para una página web diferente.
Me salmuera un dictado como:
2 representa el número que debe agregarse al siguiente nombre de archivo.
Busco el nombre de archivo cada vez del dict. Si no está allí, creo uno nuevo, agregando el número máximo si es necesario.
fuente
No es exactamente lo que estaba pidiendo OP, pero esto es lo que uso porque necesito conversiones únicas y reversibles:
El resultado es "algo" legible, al menos desde el punto de vista del administrador de sistemas.
fuente
def safe_filename(filename): return safePath(filename.strip().replace(' ','_'))
Si no le importa instalar un paquete, esto debería ser útil: https://pypi.org/project/pathvalidate/
Desde https://pypi.org/project/pathvalidate/#sanitize-a-filename :
fuente
Estoy seguro de que esta no es una gran respuesta, ya que modifica la cadena sobre la que se repite, pero parece funcionar bien:
fuente
"".join( x for x in s if (x.isalnum() or x in "._- "))
en los comentarios de esta publicaciónACTUALIZAR
Todos los enlaces rotos sin posibilidad de reparación en esta respuesta de 6 años.
Además, tampoco lo haría de esta manera, solo
base64
codificaría o soltaría caracteres inseguros. Ejemplo de Python 3:Con
base64
usted puede codificar y decodificar, para que pueda recuperar el nombre de archivo original nuevamente.Pero dependiendo del caso de uso, es mejor que genere un nombre de archivo aleatorio y almacene los metadatos en un archivo separado o DB.
RESPUESTA ORIGINAL LINKROTTEN :
El
bobcat
proyecto contiene un módulo de Python que hace exactamente esto.No es completamente robusto, mira esta publicación y esta respuesta .
Entonces, como se señaló: la
base64
codificación es probablemente una mejor idea si la legibilidad no importa.fuente