Bidouillerie du jour, bonjour.
import re import os def get_unique_path(path): # si le nom de fichier existe, on en cherche un autre while os.path.exists(path): # on vire l'extension base, ext = os.path.splitext(path) try: # on extrait le compteur si il existe base, counter, _ = re.split(r" \((\d+)\)$", base) except ValueError: counter = 0 # on reconstruit le path path = "%s (%s)%s" % (base, int(counter) + 1, ext) return path |
Le plus gros de l’astuce est dans :
base, counter, _ = re.split(r" \((\d+)\)$", base) |
\((\d+)\)$
va matcher ‘espace(un nombre)’ à la fin d’une chaîne et r.split
va retourner soit ['le chemin complet']
si il n’y a pas de compteur, soit ['base', 'compteur', '']
si il y en a un.
Du coup on unpack tout ça, _
étant utilisé pour signaler une variable inutilisée par convention et on a notre compteur, prêt à être incrémenté.
A l’usage, ça donne ça dans ipython :
>>> !rm /tmp/test* >>> get_unique_path('/tmp/test.txt') u'/tmp/test.txt' >>> get_unique_path('/tmp/test.txt') u'/tmp/test.txt' >>> !touch /tmp/test.txt >>> get_unique_path('/tmp/test.txt') u'/tmp/test (1).txt' >>> !touch "/tmp/test (1).txt" >>> get_unique_path('/tmp/test.txt') u'/tmp/test (2).txt' >>> get_unique_path('/tmp/test (101).txt') u'/tmp/test (101).txt' >>> !touch '/tmp/test (101).txt' >>> get_unique_path('/tmp/test (101).txt') u'/tmp/test (102).txt' >>> get_unique_path('/tmp/test') u'/tmp/test' >>> !touch /tmp/test >>> get_unique_path('/tmp/test') u'/tmp/test (1)' >>> get_unique_path('/tmp/.test') u'/tmp/.test' >>> !touch "/tmp/.test" >>> get_unique_path('/tmp/.test') u'/tmp/.test (1)' >>> get_unique_path('/tmp/test.path.text') u'/tmp/test.path.text' >>> !touch '/tmp/test.path.text' >>> get_unique_path('/tmp/test.path.text') u'/tmp/test.path (1).text' >>> get_unique_path('/tmp/test.path (1)(1) (1).text') u'/tmp/test.path (1)(1) (1).text' >>> !touch '/tmp/test.path (1)(1) (1).text' >>> get_unique_path('/tmp/test.path (1)(1) (1).text') u'/tmp/test.path (1)(1) (2).text' |
Bien entendu, si vos fichiers ne seront jamais visibles par l’utilisateur, il vaut mieux se simplifier la vie et utiliser uuid.uuid4().
Je pense qu’après le dossier sur les tests unitaires, je ferai un dossier regex. Aux alentours de 2018.
heeuu, j’ai pas compris l’intérêt de ça par rapport à un truc du genre tempfile.mktemp… ?
Bonjour,
\((\d+)\)$ va matcher ‘espace(un nombre)’
Du coup on unpack tout ça
Au revoir !
@JoJo: j’ai eu la même réaction, mais en fait tu peux vouloir sauvegarder un fichier sans en écraser d’autres, sans que le fichier en question soit un fichier temporaire.
que se passe t-il quand un fichier matchant la regexp est crée entre temps ?
Rien, ce n’est pas le but de gérer le concurrence, juste de trouver les noms. Si tu as besoin de faire ça dans un environnement concurrent, il faut rajouter un système de lock, ce qui doit se faire hors du contexte de cette fonction. Ou mieux, créer une architecture qui n’accède pas aux ressources de manières concurrente. Typiquement l’uuid évite le problème, et sur un serveur ça a plus de sens vu qu’aucun user ne va voir le fichier. Si c’est dans le cas d’une UI monoutilisateur, le mieux est d’ignorer le cas et de laisser crasher, ça arrivera si rarement que pour un ou deux crash en 10 ans, c’est pas suffisamment important pour se faire chier.
@JoJo : typiquement c’est pour implémenter un copier / coller qui n’écrase pas les duplicates ou ce genre de chose.
Désolé, je peux pas résister ;-)
Ben oui, ça a rien à voir avec les fichiers temporaires au final.
Tu pourrais même d’auto-linker pour l’uuid: http://sametmax.com/quest-ce-quun-uuid-et-a-quoi-ca-sert/
Et hop, done !