J'ai une chaîne que je veux utiliser comme nom de fichier, donc je veux supprimer tous les caractères qui ne seraient pas autorisés dans les noms de fichiers, en utilisant Python.
Je préfère être strict qu'autrement, alors disons que je veux conserver uniquement des lettres, des chiffres et un petit ensemble d'autres caractères comme "_-.() "
. Quelle est la solution la plus élégante?
Le nom de fichier doit être valide sur plusieurs systèmes d'exploitation (Windows, Linux et Mac OS) - c'est un fichier MP3 dans ma bibliothèque avec le titre du morceau comme nom de fichier, et est partagé et sauvegardé entre 3 machines.
os.path
charge en fait une bibliothèque différente selon le système d'exploitation (voir la deuxième note dans la documentation ). Donc, si une fonction de citation a été implémentée,os.path
elle ne peut citer que la chaîne pour POSIX-safety lors de l'exécution sur un système POSIX ou pour windows-safety lors de l'exécution sur Windows. Le nom de fichier résultant ne serait pas nécessairement valide à la fois sur Windows et POSIX, ce que demande la question.Réponses:
Vous pouvez regarder le framework Django pour voir comment ils créent un "slug" à partir de texte arbitraire. Un slug est compatible URL et nom de fichier.
Les utilitaires de texte Django définissent une fonction,
slugify()
c'est probablement l'étalon-or pour ce genre de chose. Essentiellement, leur code est le suivant.Il y a plus, mais je l'ai laissé de côté, car il ne traite pas de la slugification, mais de l'évasion.
la source
value
. Si la valeur doit être Unicode, alors, vous devez être sûr qu'il s'agit bien d'Unicode. Ou. Vous souhaiterez peut-être ignorer la normalisation Unicode si votre valeur réelle est en fait une chaîne ASCII.slugify
fonction a été déplacée dans django / utils / text.py , et ce fichier contient également uneget_valid_filename
fonction.Cette approche de liste blanche (c'est-à-dire, autoriser uniquement les caractères présents dans valid_chars) fonctionnera s'il n'y a pas de limites sur le formatage des fichiers ou une combinaison de caractères valides qui sont illégaux (comme ".."), par exemple, ce que vous dites permettrait un nom de fichier nommé ". txt" qui, je pense, n'est pas valide sous Windows. Comme c'est l'approche la plus simple, j'essaierais de supprimer les espaces blancs des valid_chars et d'ajouter une chaîne valide connue en cas d'erreur, toute autre approche devra savoir ce qui est autorisé où faire face aux limitations de nommage des fichiers Windows et donc être beaucoup plus complexe.
la source
valid_chars = frozenset(valid_chars)
ne ferait pas de mal. Il est 1,5 fois plus rapide s'il est appliqué à allchars."CON"
sous Windows vous causera des ennuis ...Vous pouvez utiliser la compréhension de liste avec les méthodes de chaîne.
la source
filename = "".join(i for i in s if i not in "\/:*?<>|")
"".join( x for x in s if (x.isalnum() or x in "._- "))
Quelle est la raison d'utiliser les chaînes comme noms de fichiers? Si la lisibilité humaine n'est pas un facteur, j'irais avec le module base64 qui peut produire des chaînes sûres du système de fichiers. Il ne sera pas lisible mais vous n'aurez pas à gérer les collisions et il est réversible.
Mise à jour : modifié en fonction du commentaire de Matthew.
la source
your_string
doit être un tableau d'octets ou le résultat deencode('ascii')
pour que cela fonctionne.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')
Juste pour compliquer encore les choses, vous n'êtes pas assuré d'obtenir un nom de fichier valide simplement en supprimant les caractères non valides. Étant donné que les caractères autorisés diffèrent selon les noms de fichiers différents, une approche conservatrice pourrait finir par transformer un nom valide en nom invalide. Vous voudrez peut-être ajouter un traitement spécial pour les cas où:
La chaîne est composée de tous les caractères invalides (vous laissant avec une chaîne vide)
Vous vous retrouvez avec une chaîne avec une signification spéciale, par exemple "." ou ".."
Sous Windows, certains noms d'appareils sont réservés. Par exemple, vous ne pouvez pas créer un fichier nommé "nul", "nul.txt" (ou nul.anything en fait) Les noms réservés sont:
CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8 et LPT9
Vous pouvez probablement contourner ces problèmes en ajoutant une chaîne aux noms de fichiers qui ne peuvent jamais entraîner l'un de ces cas et en supprimant les caractères non valides.
la source
Il y a un joli projet sur Github appelé python-slugify :
Installer:
Utilisez ensuite:
la source
test.txt
obtient cetest-txt
qui est trop.Tout comme S.Lott a répondu, vous pouvez regarder le Framework Django pour voir comment ils convertissent une chaîne en un nom de fichier valide.
La version la plus récente et mise à jour se trouve dans utils / text.py et définit "get_valid_filename", qui est la suivante:
(Voir https://github.com/django/django/blob/master/django/utils/text.py )
la source
django.utils.text import get_valid_filename
re.sub(r'(?u)[^-\w.]', '', s)
supprime tous les caractères qui ne sont pas des lettres, pas des chiffres (0-9), pas le trait de soulignement ('_'), pas le tiret ('-') et pas le point ('.' ). "Lettres" comprend ici toutes les lettres Unicode, telles que 漢語.Voici la solution que j'ai finalement utilisée:
L'appel unicodedata.normalize remplace les caractères accentués par l'équivalent non accentué, ce qui est mieux que simplement les supprimer. Après cela, tous les caractères interdits sont supprimés.
Ma solution ne ajoute pas de chaîne connue pour éviter d'éventuels noms de fichiers non autorisés, car je sais qu'ils ne peuvent pas se produire compte tenu de mon format de nom de fichier particulier. Une solution plus générale devrait le faire.
la source
Gardez à l'esprit qu'il n'y a en fait aucune restriction sur les noms de fichiers sur les systèmes Unix autres que
Tout le reste est un jeu équitable.
Oui, je viens de stocker les codes de couleur ANSI dans un nom de fichier et de les faire prendre effet.
Pour le divertissement, mettez un caractère BEL dans un nom de répertoire et regardez le plaisir qui s'ensuit lorsque vous y insérez un CD;)
la source
En une seule ligne:
vous pouvez également mettre le caractère '_' pour le rendre plus lisible (en cas de remplacement de barres obliques, par exemple)
la source
Vous pouvez utiliser la méthode re.sub () pour remplacer tout ce qui n'est pas "filelike". Mais en effet, chaque caractère pourrait être valide; il n'y a donc pas de fonctions prédéfinies (je crois), pour le faire.
Cela entraînerait un descripteur de fichier dans /tmp/filename.txt.
la source
Il ne gère pas les chaînes vides, les noms de fichiers spéciaux («nul», «con», etc.).
la source
Mais il faut être prudent. Ce n'est pas clairement dit dans votre intro, si vous ne regardez que la langue latine. Certains mots peuvent perdre leur sens ou un autre sens si vous les désinfectez uniquement avec des caractères ascii.
imaginez que vous avez "forêt poésie" (poésie forestière), votre assainissement pourrait donner "fort-posie" (fort + quelque chose de dénué de sens)
Pire encore si vous devez gérer des caractères chinois.
"下 北 沢" votre système pourrait finir par faire "---" qui est voué à l'échec après un certain temps et n'est pas très utile. Donc, si vous ne traitez qu'avec des fichiers, j'encourage à les appeler une chaîne générique que vous contrôlez ou à conserver les caractères tels quels. Pour les URI, à peu près la même chose.
la source
Pourquoi ne pas simplement envelopper le "osopen" avec un try / except et laisser le système d'exploitation sous-jacent trier si le fichier est valide?
Cela semble beaucoup moins de travail et est valable quel que soit le système d'exploitation que vous utilisez.
la source
osopen
fonctionnement sur une seule machine.Un autre problème que les autres commentaires n'ont pas encore résolu est la chaîne vide, qui n'est évidemment pas un nom de fichier valide. Vous pouvez également vous retrouver avec une chaîne vide en supprimant trop de caractères.
Qu'en est-il des noms de fichiers réservés de Windows et des problèmes avec les points, la réponse la plus sûre à la question "comment normaliser un nom de fichier valide à partir d'une entrée utilisateur arbitraire?" est "ne vous embêtez même pas": si vous pouvez trouver un autre moyen de l'éviter (par exemple en utilisant des clés primaires entières d'une base de données comme noms de fichiers), faites-le.
Si vous devez, et vous devez vraiment autoriser les espaces et '.' pour les extensions de fichier dans le nom, essayez quelque chose comme:
Même cela ne peut pas être garanti correctement, en particulier sur les systèmes d'exploitation inattendus - par exemple, RISC OS déteste les espaces et utilise ''. comme séparateur de répertoire.
la source
J'ai aimé l'approche python-slugify ici, mais elle supprimait également les points, ce qui n'était pas souhaité. Je l'ai donc optimisé pour télécharger un nom de fichier propre vers s3 de cette façon:
Exemple de code:
Production:
C'est tellement sûr, cela fonctionne avec des noms de fichiers sans extension et cela ne fonctionne même que pour les noms de fichiers de caractères dangereux (le résultat est
none
ici).la source
Réponse modifiée pour python 3.6
la source
Je me rends compte qu'il existe de nombreuses réponses, mais elles reposent principalement sur des expressions régulières ou des modules externes, donc je voudrais ajouter ma propre réponse. Une fonction python pure, aucun module externe nécessaire, aucune expression régulière utilisée. Mon approche n'est pas de nettoyer les caractères invalides, mais de n'autoriser que ceux valides.
si vous le souhaitez, vous pouvez ajouter vos propres caractères valides à la
validchars
variable au début, comme vos lettres nationales qui n'existent pas dans l'alphabet anglais. C'est quelque chose que vous pouvez ou ne voulez pas: certains systèmes de fichiers qui ne fonctionnent pas sur UTF-8 peuvent toujours avoir des problèmes avec les caractères non ASCII.Cette fonction consiste à tester la validité d'un nom de fichier unique, elle remplacera donc les séparateurs de chemin par _ les considérant comme des caractères non valides. Si vous souhaitez ajouter cela, il est trivial de modifier le
if
pour inclure le séparateur de chemin os.la source
La plupart de ces solutions ne fonctionnent pas.
'/ bonjour / monde' -> 'helloworld'
'/ helloworld' / -> 'helloworld'
Ce n'est pas ce que vous voulez en général, disons que vous enregistrez le code HTML pour chaque lien, vous allez remplacer le code HTML pour une page Web différente.
Je décapage un dicton tel que:
2 représente le numéro qui doit être ajouté au nom de fichier suivant.
Je recherche le nom de fichier à chaque fois dans le dict. S'il n'est pas là, j'en crée un nouveau, en ajoutant le nombre maximum si nécessaire.
la source
Pas exactement ce qu'OP demandait mais c'est ce que j'utilise car j'ai besoin de conversions uniques et réversibles:
Le résultat est "quelque peu" lisible, du moins d'un point de vue administrateur système.
la source
def safe_filename(filename): return safePath(filename.strip().replace(' ','_'))
Si cela ne vous dérange pas d'installer un package, cela devrait être utile: https://pypi.org/project/pathvalidate/
Depuis https://pypi.org/project/pathvalidate/#sanitize-a-filename :
la source
Je suis sûr que ce n'est pas une bonne réponse, car cela modifie la chaîne sur laquelle il boucle, mais cela semble bien fonctionner:
la source
"".join( x for x in s if (x.isalnum() or x in "._- "))
sur ce post commentairesMETTRE À JOUR
Tous les liens sont irréparables dans cette réponse de 6 ans.
De plus, je ne le ferais plus de cette façon, juste
base64
encoder ou supprimer des caractères dangereux. Exemple Python 3:Avec
base64
vous pouvez encoder et décoder, vous pouvez donc récupérer à nouveau le nom de fichier d'origine.Mais selon le cas d'utilisation, il serait préférable de générer un nom de fichier aléatoire et de stocker les métadonnées dans un fichier ou une base de données séparé.
RÉPONSE ORIGINALE LINKROTTEN :
Le
bobcat
projet contient un module python qui fait exactement cela.Ce n'est pas complètement robuste, voir ce post et cette réponse .
Donc, comme indiqué: l'
base64
encodage est probablement une meilleure idée si la lisibilité n'a pas d'importance.la source