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()
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 .
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 .
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!
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.
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.
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()
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)
Les deux manières de le faire sont:
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
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")
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')