web-dev-qa-db-fra.com

Comment créer un nom de fichier incrémentant en Python?

Je crée un programme qui va créer un fichier et le sauvegarder dans le répertoire portant le nom de fichier sample.xml. Une fois le fichier enregistré lorsque j'essaie de réexécuter le programme, il écrase l'ancien fichier dans le nouveau car ils portent le même nom. Comment incrémenter les noms de fichiers pour que, chaque fois que j'essaie de réexécuter le code, il incrémente le nom du fichier. et ne remplacera pas l'existant. Je songe à vérifier le nom de fichier en premier sur le répertoire et s’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()
19
Oliver Ven Quilnet

Je voudrais parcourir sample[int].xml par exemple et saisir le prochain nom disponible 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 au début, puis sample1.xml , etc.

Notez que la notation de fichier relative par défaut se rapporte au répertoire/dossier de fichiers à partir duquel vous avez exécuté le code. Utilisez des chemins absolus si nécessaire. Utilisez os.getcwd() pour lire votre répertoire current et os.chdir(path_to_dir) pour définir un nouveau répertoire current .

38
bossi
def get_nonexistant_path(fname_path):
    """
    Get the path to a filename which does not exist by incrementing path.

    Examples
    --------
    >>> 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 déjà.

Je recommande d'utiliser os.path.abspath("sample.xml"). Si vous avez ~ comme répertoire de base, vous devrez peut-être développez-le first.

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

10
Martin Thoma

Essayez de définir une variable de nombre, puis d’incrémenter cette variable imbriquée dans la même boucle dans laquelle vous écrivez votre fichier. Incluez la boucle de comptage dans le nom du fichier avec un caractère d’échappement. fichier.

Du code d'un projet que je viens de terminer:

numberLoops = #some limit determined by the user
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 n’est probablement pas pertinent dans votre cas mais j’exécutais plusieurs instances de ce programme et produisais des tonnes de fichiers. J'espère que cela t'aides!

4
ford

La vérification séquentielle de chaque nom de fichier pour trouver le prochain nom disponible fonctionne correctement avec un petit nombre de fichiers, mais devient rapidement plus lente lorsque le nombre de fichiers augmente.

Voici une version qui trouve le prochain nom de fichier disponible dans log (n) time:

import os

def next_path(path_pattern):
    """
    Finds the next free path in an sequentially named list of files

    e.g. path_pattern = 'file-%s.txt':

    file-1.txt
    file-2.txt
    file-3.txt

    Runs in log(n) time where n is the number of existing files in sequence
    """
    i = 1

    # First do an exponential search
    while os.path.exists(path_pattern % i):
        i = i * 2

    # Result lies somewhere in the interval (i/2..i]
    # We call this interval (a..b] and narrow it down until a + 1 = b
    a, b = (i / 2, i)
    while a + 1 < b:
        c = (a + b) / 2 # interval midpoint
        a, b = (c, b) if os.path.exists(path_pattern % c) else (a, c)

    return path_pattern % b

Pour mesurer l'amélioration de la 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('file-%s.foo'), 'w'):
        pass

Et mis en œuvre l'approche naïve:

def next_path_naive(path_pattern):
    """
    Naive (slow) version of next_path
    """
    i = 1
    while os.path.exists(path_pattern % i):
        i += 1
    return path_pattern % i

Et voici les résultats:

Version rapide:

real    0m2.132s
user    0m0.773s
sys 0m1.312s

Version naïve:

real    2m36.480s
user    1m12.671s
sys 1m22.425s

Enfin, notez que l'une ou l'autre approche est susceptible aux conditions de concurrence si plusieurs acteurs tentent de créer des fichiers dans la séquence en même temps.

4
James

Vous pouvez utiliser une boucle while avec un compteur qui vérifie si un fichier avec un nom et la valeur du compteur existe s'il le fait, puis passe à la casse et crée un fichier.

Je l'ai fait de cette manière pour l'un de mes projets:

from os import path
import os

i = 0
flnm = "Directory\\Filename" + str(i) + ".txt"
while path.exists(flnm) :
    flnm = "Directory\\Filename" + str(i) + ".txt"
    i += 1
f = open(flnm, "w") #do what you want to with that file...
f.write(str(var))
f.close() # make sure to close it.

`

Ici, le compteur i commence à 0 et une boucle while vérifie à chaque fois si le fichier existe, s'il est déplacé, il éclate et crée un fichier que vous pouvez personnaliser. Assurez-vous également de le fermer, sinon le fichier sera ouvert, ce qui peut poser des problèmes lors de sa suppression. J'ai utilisé path.exists () pour vérifier si un fichier existe. ... Ne faites pas from os import * cela peut causer des problèmes lorsque nous utilisons la méthode open (), car il existe également une autre méthode os.open () qui peut également générer l'erreur. TypeError: Integer expected. (got str) Sinon, nous vous souhaitons une bonne année et à tous.

1
Matrix Programmer

Sans stocker les données d'état dans un fichier supplémentaire, une solution plus rapide à ceux présentés ici serait la suivante:

from glob import glob
import os

files = glob("somedir/sample*.xml")
files = files.sorted()
cur_num = int(os.path.basename(files[-1])[6:-4])
cur_num += 1
fh = open("somedir/sample%s.xml" % cur_num, 'w')
rs = [blockresult]
fh.writelines(rs)
fh.close()

Cela continuera également à augmenter, même si certains des fichiers numérotés les plus bas disparaissent.

L’autre solution que j’aime bien (soulignée par Eiyrioü) consiste à conserver un fichier temporaire contenant votre numéro le plus récent:

temp_fh = open('somedir/curr_num.txt', 'r')
curr_num = int(temp_fh.readline().strip())
curr_num += 1
fh = open("somedir/sample%s.xml" % cur_num, 'w')
rs = [blockresult]
fh.writelines(rs)
fh.close()
1
Vorticity

Un autre exemple utilisant la récursivité 

import os
def checkFilePath(testString, extension, currentCount):
    if os.path.exists(testString + str(currentCount) +extension):
        return checkFilePath(testString, extension, currentCount+1)
    else:
        return testString + str(currentCount) +extension

Utilisation:

checkFilePath("myfile", ".txt" , 0)
1
chumbaloo

Les deux manières de le faire sont: 

  1. Vérifiez l'existence de l'ancien fichier et s'il existe, essayez le nom de fichier suivant +1
  2. sauvegarder les données d'état quelque part

un moyen facile de le faire dès le départ 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 design, while True ralentit les choses et n’est pas une très bonne chose pour la lisibilité du code


édité: @EOL contributions/thoughts

donc je pense que ne pas avoir .format est plus lisible au premier abord - mais utiliser .format est préférable 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')
# or 
my_next_file = open(filename + "{}.py".format(filenum),'w')

et vous n'avez pas à utiliser abspath - vous pouvez utiliser des chemins relatifs si vous préférez, je préfère parfois les chemins abs car cela aide à normaliser les chemins passés :).

import os.path as pth
filename = "myfile"
filenum = 1
while (pth.exists(filename+str(filenum)+".py"):
    filenum+=1
##removed for conciseness
0
Eiyrioü von Kauyf

Je devais faire quelque chose de similaire, mais pour les répertoires de sortie dans un pipeline de traitement de données. La réponse de Vorticity m'a inspiré, mais l'utilisation de regex a été ajoutée pour saisir le nombre final. Cette méthode continue à incrémenter le dernier répertoire, même si les répertoires de sortie numérotés intermédiaires sont supprimés. Il ajoute également des zéros à gauche afin que les noms soient triés par ordre alphabétique (c'est-à-dire que la largeur 3 indique 001, etc.).

def get_unique_dir(path, width=3):
    # if it doesn't exist, create
    if not os.path.isdir(path):
        log.debug("Creating new directory - {}".format(path))
        os.makedirs(path)
        return path

    # if it's empty, use
    if not os.listdir(path):
        log.debug("Using empty directory - {}".format(path))
        return path

    # otherwise, increment the highest number folder in the series

    def get_trailing_number(search_text):
        serch_obj = re.search(r"([0-9]+)$", search_text)
        if not serch_obj:
            return 0
        else:
            return int(serch_obj.group(1))

    dirs = glob(path + "*")
    num_list = sorted([get_trailing_number(d) for d in dirs])
    highest_num = num_list[-1]
    next_num = highest_num + 1
    new_path = "{0}_{1:0>{2}}".format(path, next_num, width)

    log.debug("Creating new incremented directory - {}".format(new_path))
    os.makedirs(new_path)
    return new_path

get_unique_dir("output")
0
Woods26

Une autre solution permettant d’éviter l’utilisation de la boucle while consiste à utiliser la fonction os.listdir() qui renvoie une liste de tous les fichiers et répertoires contenus dans un répertoire dont le chemin est pris comme argument.

Pour répondre à l'exemple de la question, en supposant que le répertoire dans lequel vous travaillez ne contient que des fichiers "sample_i.xlm" indexés à partir de 0, vous pouvez facilement obtenir le prochain index du nouveau fichier avec le code suivant.

import os

new_index = len(os.listdir('path_to_file_containing_only_sample_i_files'))
new_file = open('path_to_file_containing_only_sample_i_files/sample_%s.xml' % new_index, 'w')
0
Malo Pocheau