Python: create file, but if name exists add number

Are there any built-in functions in Python to add a number to a file name if it already exists?

My idea is that it will work the way a certain OS works - if a file is displayed in a directory where a file with the same name already exists, it adds a number or increases it.

That is: if the file "file.pdf" exists, it will create the file "file2.pdf", and next time "file3.pdf".

+15
source share
12 answers

In a way, Python has this functionality built into the tempfile module. Unfortunately, you need to connect to the private global variable tempfile._name_sequence . This means that officially tempfile does not guarantee that in future versions of _name_sequence even exists - this is an implementation detail. But if you use it anyway, it will show how you can create uniquely named files of the form file#.pdf in the specified directory, for example /tmp :

 import tempfile import itertools as IT import os def uniquify(path, sep = ''): def name_sequence(): count = IT.count() yield '' while True: yield '{s}{n:d}'.format(s = sep, n = next(count)) orig = tempfile._name_sequence with tempfile._once_lock: tempfile._name_sequence = name_sequence() path = os.path.normpath(path) dirname, basename = os.path.split(path) filename, ext = os.path.splitext(basename) fd, filename = tempfile.mkstemp(dir = dirname, prefix = filename, suffix = ext) tempfile._name_sequence = orig return filename print(uniquify('/tmp/file.pdf')) 
+12
source

I tried to implement the same thing in my project, but @unutbu's answer seemed too "heavy" for my needs, so I finally came up with the following code:

 import os index = '' while True: try: os.makedirs('../hi'+index) break except WindowsError: if index: index = '('+str(int(index[1:-1])+1)+')' # Append 1 to number in brackets else: index = '(1)' pass # Go and try create file again 

Just in case, someone came across this and requires something simpler.

+3
source

Since tempfile hack A) is a hack and B) still requires a decent amount of code, I went with a manual implementation. You basically need to:

I defined safe_open, which can be used just like open:

 def iter_incrementing_file_names(path): """ Iterate incrementing file names. Start with path and add " (n)" before the extension, where n starts at 1 and increases. :param path: Some path :return: An iterator. """ yield path prefix, ext = os.path.splitext(path) for i in itertools.count(start=1, step=1): yield prefix + ' ({0})'.format(i) + ext def safe_open(path, mode): """ Open path, but if it already exists, add " (n)" before the extension, where n is the first number found such that the file does not already exist. Returns an open file handle. Make sure to close! :param path: Some file name. :return: Open file handle... be sure to close! """ flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY if 'b' in mode and platform.system() == 'Windows': flags |= os.O_BINARY for filename in iter_incrementing_file_names(path): try: file_handle = os.open(filename, flags) except OSError as e: if e.errno == errno.EEXIST: pass else: raise else: return os.fdopen(file_handle, mode) # Example with safe_open("some_file.txt", "w") as fh: print("Hello", file=fh) 
+3
source

If the numbering of all files is not a problem, and you know in advance the name of the file to be written, you can simply do:

 import os counter = 0 filename = "file{}.pdf" while os.path.isfile(filename.format(counter)): counter += 1 filename = filename.format(counter) 
+3
source

I recently came across the same, and here is my approach:

 import os file_name = "file_name.txt" if os.path.isfile(file_name): expand = 1 while True: expand += 1 new_file_name = file_name.split(".txt")[0] + str(expand) + ".txt" if os.path.isfile(new_file_name): continue else: file_name = new_file_name break 
+2
source

I have not tested this yet, but it should work, iterating over the possible file names, until this file exists, at what point it will break.

 def increment_filename(fn): fn, extension = os.path.splitext(path) n = 1 yield fn + extension for n in itertools.count(start=1, step=1) yield '%s%d.%s' % (fn, n, extension) for filename in increment_filename(original_filename): if not os.isfile(filename): break 
0
source

This works for me. The original 0.yml file 0.yml , if it exists, will add it until it meets the requirements

 import os import itertools def increment_filename(file_name): fid, extension = os.path.splitext(file_name) yield fid + extension for n in itertools.count(start=1, step=1): new_id = int(fid) + n yield "%s%s" % (new_id, extension) def get_file_path(): target_file_path = None for file_name in increment_filename("0.yml"): file_path = os.path.join('/tmp', file_name) if not os.path.isfile(file_path): target_file_path = file_path break return target_file_path 
0
source
 import os class Renamer(): def __init__(self, name): self.extension = name.split('.')[-1] self.name = name[:-len(self.extension)-1] self.filename = self.name def rename(self): i = 1 if os.path.exists(self.filename+'.'+self.extension): while os.path.exists(self.filename+'.'+self.extension): self.filename = '{} ({})'.format(self.name,i) i += 1 return self.filename+'.'+self.extension 
0
source

I implemented a similar solution with pathlib :

Create file names that match the pattern path/<file-name>-\d\d.ext . Perhaps this solution will help ...

 import pathlib from toolz import itertoolz as itz def file_exists_add_number(path_file_name, digits=2): pfn = pathlib.Path(path_file_name) parent = pfn.parent # parent-dir of file stem = pfn.stem # file-name w/o extension suffix = pfn.suffix # NOTE: extension starts with '.' (dot)! try: # search for files ending with '-\d\d.ext' last_file = itz.last(parent.glob(f"{stem}-{digits * '?'}{suffix}")) except: curr_no = 1 else: curr_no = int(last_file.stem[-digits:]) + 1 # int to string and add leading zeros curr_no = str(last_no).zfill(digits) path_file_name = parent / f"{stem}-{curr_no}{suffix}" return str(path_file_name) 

Note: this solution starts at 01 and will only search for a sample file containing -\d\d !

0
source

I wrote my simple function for this. Primitively, but it does its job:

 def uniquify(path): filename, extension = os.path.splitext(path) counter = 1 while os.path.exists(path): path = filename + " (" + str(counter) + ")" + extension counter += 1 return path 
0
source

I found that the os.path.exists () conditional function does what I need. As an example, I use save from dictionary in csv, but the same logic can work for files of any type:

 import os def smart_save(filename, dict): od = filename + '_' # added underscore before number for clarity for i in np.arange(0,500,1): # I set an arbitrary upper limit of 500 d = od + str(i) if os.path.exists(d + '.csv'): pass else: with open(d + '.csv', 'w') as f: #or any saving operation you need for key in dict.keys(): f.write("%s,%s\n"%(key, dictionary[key])) break 

Note: by default, a number is added to the file name (starting at 0), but it is easy to shift it.

0
source

A little later, but still something like this should work properly, m. will be useful to someone.

You can use the built-in iterator to do this (an example image loader for you):

 def image_downloader(): image_url = 'some_image_url' for count in range(10): image_data = requests.get(image_url).content with open(f'image_{count}.jpg', 'wb') as handler: handler.write(image_data) 

Files will grow correctly. Result:

 image.jpg image_0.jpg image_1.jpg image_2.jpg image_3.jpg image_4.jpg image_5.jpg image_6.jpg image_7.jpg image_8.jpg image_9.jpg 
-1
source

All Articles