45 votes

Comment créer un nom de fichier s'incrémentant

Je crée un programme qui va créer un fichier et le sauvegarder dans le répertoire avec le nom de fichier sample.xml. Une fois le fichier enregistré, lorsque j'essaie d'exécuter le programme à nouveau, il écrase l'ancien fichier par le nouveau parce qu'ils ont le même nom de fichier. Comment puis-je incrémenter les noms de fichiers pour que chaque fois que je tente d'exécuter le code à nouveau, il va incrémenter le nom de fichier et ne pas écraser l'ancien. Je pense à vérifier d'abord le nom de fichier dans le répertoire et si ils sont identiques, le code générera un nouveau nom de fichier :

fh = open("sample.xml", "w")
rs = [blockresult]
fh.writelines(rs)
fh.close()

73voto

bossi Points 1407

Je parcourrais sample[int].xml par exemple et je récupérerais le nom disponible suivant qui n'est pas utilisé par un fichier ou un répertoire.

import os

i = 0
while os.path.exists("sample%s.xml" % i):
    i += 1

fh = open("sample%s.xml" % i, "w")
....

Cela devrait vous donner sample0.xml initialement, puis sample1.xml, etc.

Remarquez que la notation de fichier relative par défaut se rapporte au répertoire dans lequel vous exécutez le code. Utilisez des chemins absolus si nécessaire. Utilisez os.getcwd() pour lire votre répertoire actuel et os.chdir(chemin_vers_répertoire) pour définir un nouveau répertoire actuel.

26voto

James Points 886

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.

16voto

moose Points 4945
def get_nonexistant_path(fname_path):
    """
    Obtenez le chemin d'accès à un fichier qui n'existe pas en incrémentant le chemin.

    Exemples
    --------
    >>> get_nonexistant_path('/etc/issue')
    '/etc/issue-1'
    >>> get_nonexistant_path('whatever/1337bla.py')
    'whatever/1337bla.py'
    """
    if not os.path.exists(fname_path):
        return fname_path
    filename, file_extension = os.path.splitext(fname_path)
    i = 1
    new_fname = "{}-{}{}".format(filename, i, file_extension)
    while os.path.exists(new_fname):
        i += 1
        new_fname = "{}-{}{}".format(filename, i, file_extension)
    return new_fname

Avant d'ouvrir le fichier, appelez

fname = get_nonexistant_path("sample.xml")

Cela vous donnera soit 'sample.xml' soit - si cela existe déjà - 'sample-i.xml' où i est le plus petit entier positif tel que le fichier n'existe pas encore.

Je recommande d'utiliser os.path.abspath("sample.xml"). Si vous avez ~ comme répertoire personnel, vous devrez peut-être l'étendre d'abord.

Veuillez noter que des conditions de concurrence peuvent se produire avec ce code simple si vous avez plusieurs instances s'exécutant en même temps. Si cela peut poser un problème, veuillez consulter cette question.

6voto

ford Points 170

Essayez de définir une variable de comptage, puis d'incrémenter cette variable imbriquée dans la même boucle à l'intérieur de laquelle vous écrivez votre fichier. Incluez la boucle de comptage dans le nom du fichier avec un caractère d'échappement, afin que chaque boucle fasse tic-tac +1 et que le numéro dans le fichier fasse de même.

Voici un extrait de code d'un projet que je viens de terminer :

numberLoops = #limite déterminée par l'utilisateur
currentLoop = 1
while currentLoop < numberLoops :
    currentLoop = currentLoop + 1

    fileName = ("log%d_%d.txt" % (currentLoop, str(now())))

Pour référence:

from time import mktime, gmtime

def now() : 
   return mktime(gmtime()) 

ce qui est probablement sans rapport dans votre cas, mais je faisais tourner plusieurs instances de ce programme et créais des tonnes de fichiers. J'espère que cela vous sera utile !

3voto

Les deux façons de le faire sont :

  1. Vérifier l'existence du vieux fichier et s'il existe, essayez le nom de fichier suivant +1
  2. enregistrer les données de l'état quelque part

Une façon facile de le faire immédiatement serait :

import os.path as pth
filename = "myfile"
filenum = 1
while (pth.exists(pth.abspath(filename+str(filenum)+".py")):
    filenum+=1
my_next_file = open(filename+str(filenum)+".py",'w')

en tant que concept de conception, while True ralentit les choses et n'est pas une bonne chose pour la lisibilité du code


modifié : @EOL contributions/pensées

donc je pense qu'au premier coup d'œil, ne pas avoir .format est plus lisible - mais utiliser .format est meilleur pour la généralité et la convention donc.

import os.path as pth
filename = "myfile"
filenum = 1
while (pth.exists(pth.abspath(filename+str(filenum)+".py")):
    filenum+=1
my_next_file = open("{}{}.py".format(filename, filenum),'w')
# ou
my_next_file = open(filename + "{}.py".format(filenum),'w')

et vous n'êtes pas obligé d'utiliser abspath - vous pouvez utiliser des chemins relatifs si vous préférez, j'aime parfois utiliser des chemins absolus car cela aide à normaliser les chemins transmis :).

import os.path as pth
filename = "myfile"
filenum = 1
while (pth.exists(filename+str(filenum)+".py"):
    filenum+=1
##supprimé pour plus de concision

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X