Vérifier séquentiellement chaque nom de fichier pour trouver le suivant disponible fonctionne bien avec un petit nombre de fichiers, mais devient rapidement plus lent à mesure que le nombre de fichiers augmente.
Voici une version qui trouve le nom de fichier disponible suivant en temps log(n):
import os
def next_path(path_pattern):
"""
Trouve le prochain chemin libre dans une liste de fichiers numérotés séquentiellement
par exemple, path_pattern = 'fichier-%s.txt':
fichier-1.txt
fichier-2.txt
fichier-3.txt
S'exécute en temps log(n) où n est le nombre de fichiers existants dans la séquence
"""
i = 1
# On commence par une recherche exponentielle
while os.path.exists(path_pattern % i):
i = i * 2
# Le résultat se trouve quelque part dans l'intervalle (i/2..i]
# On appelle cet intervalle (a..b] et on le réduit jusqu'à ce que a + 1 = b
a, b = (i // 2, i)
while a + 1 < b:
c = (a + b) // 2 # milieu de l'intervalle
a, b = (c, b) if os.path.exists(path_pattern % c) else (a, c)
return path_pattern % b
Pour mesurer l'amélioration de vitesse, j'ai écrit une petite fonction de test qui crée 10 000 fichiers:
for i in range(1,10000):
with open(next_path('fichier-%s.foo'), 'w'):
pass
Et implémenté l'approche naïve:
def next_path_naive(path_pattern):
"""
Version naïve (lente) de next_path
"""
i = 1
while os.path.exists(path_pattern % i):
i += 1
return path_pattern % i
Et voici les résultats:
Version rapide:
réel 0m2.132s
utilisateur 0m0.773s
système 0m1.312s
Version naïve:
réel 2m36.480s
utilisateur 1m12.671s
système 1m22.425s
Enfin, notez que les deux approches sont sensibles aux conditions de course si plusieurs acteurs essaient de créer des fichiers dans la séquence en même temps.