I wrote a program to rename files with consecutive numbers, basically removing the gaps in the numbering. This could be a very annoying task to do it manually in windows. I only did this for practicing Python purposes.
Lets show what the program does in an example:
Say you have a folder
E:\Spam which contains the following files:
Lets say for what reason you want to continues numbe all the files from type
After running my program it gives out this:
eggs010.txt (unchanged wrong filename)
spam5.dat (unchanged wrong filetype)
Also an offset can be added to start the numbering for example at 5 or 0 instead of.
""" Filling_gaps. Removes gaps in numbering of multiple files in a specified directory. Takes a directory to search for files which match prefix and type and otherwise only contain numbers in the middle. Files are renamed continuesyly and filled up with leading zeros if necessary It is possible to define with which number the numeration starts e.g. spam001.txt spam003.txt spam013.txt spam1180.txt would become (if start_number = 1): spam0001.txt spam0002.txt spam0003.txt spam0004.txt To prevent overriting in the renaming process a temporary directory is created during the renaming. """ import os import sys import shutil import stat from typing import List def get_filenames(directory: str) -> List[str]: """ get filenames in supplied absolute directory name """ os.chdir(directory) return os.listdir('.') def get_digits_minimum_length(filenames: List[str], file_prefix: str, file_type: str, start_number: int) -> int: """ loops over all provided filenames to find the filename with the longest digit string e.g: spam00001.txt -> len = 5 spam02.txt -> len = 2 spam0000003.txt -> len = 7 digits_length == 7 """ digits_length: int = 0 for filename in filenames: if filename.startswith(file_prefix) and filename.endswith(file_type): number_string: str = filename.lstrip(file_prefix) number_string = number_string.rstrip(file_type) if number_string.isdigit(): current_length = len(number_string) if current_length > digits_length: digits_length = current_length if len(str(start_number)) > digits_length: digits_length = start_number return digits_length def make_filename(file_prefix: str, file_type: str, number: int, number_length: int) -> str: """ Creates a new filename out of the parts. """ return file_prefix + str(number).zfill(number_length) + file_type def make_folder(directory: str, folder_name: str): """ Creates a folder inside a directory """ new_directory: str = os.path.join(directory, folder_name) os.makedirs(new_directory) os.chmod(new_directory, stat.S_IWRITE) def rename_and_move_file(directory: str, new_directory: str, old_filename: str, new_filename: str): """ Renames a file from a specific directory into a new directory to prevent that renamoving overrites other existing files """ shutil.move(os.path.join(directory, old_filename), os.path.join(new_directory, new_filename)) def erase_directory(delete_directory: str): """ checks if directory exists and deletes it. """ if os.path.exists(delete_directory): shutil.rmtree(delete_directory) def extract_digits_and_move_to_temp( directory: str, temp_directory: str, file_prefix: str, file_type: str, digit_lenght: int): """ Remove pre und postfix from files, add leading zeros to the integer part to reach the same length for all digitsand copy them to temp directory """ for filename in get_filenames(directory): if filename.startswith(file_prefix) and filename.endswith(file_type): number_string: str = filename.lstrip(file_prefix) number_string = number_string.rstrip(file_type) if number_string.isdigit(): rename_and_move_file(directory, temp_directory, filename, number_string.zfill(digit_lenght)) def renumber_and_move_to_directory( start_number: int, directory: str, temp_directory: str, file_prefix: str, file_type: str, digit_lenght: int): """ Renumbers the files according to start file number and moves them back to directory """ next_number: int = start_number filenames = get_filenames(temp_directory) filenames.sort() for filename in filenames: new_filename: str = make_filename( file_prefix, file_type, next_number, digit_lenght) rename_and_move_file(temp_directory, directory, filename, new_filename) next_number = next_number + 1 def filling_gaps(directory: str, file_prefix: str, file_type: str, start_number: int = 1): """ Main logic of the filling gaps script. First a temporary directory is made for the file operations. Then the max len of digits (int + leading zeros) is calculated out of the files. Then all matched files are stripped everything but the int part and they are moved into the temp directory. In the temp directory they are numerated correctly again and moved back to the source directory. In the end the temporary directory is deleted """ if start_number < 0: raise Exception("Start file number must be >=0. Value: " + str(start_number)) temp_folder_name = "temp" temp_directory = os.path.join(directory, temp_folder_name) if os.path.exists(temp_directory): print("Error: temp directory used for internal copy already exists." " Make sure it does not. directory name:" + temp_folder_name) sys.exit(0) make_folder(directory, temp_folder_name) digits_length: int = get_digits_minimum_length( get_filenames(directory), file_prefix, file_type, start_number) extract_digits_and_move_to_temp( directory, temp_directory, file_prefix, file_type, digits_length) renumber_and_move_to_directory( start_number, directory, temp_directory, file_prefix, file_type, digits_length) erase_directory(temp_directory) filling_gaps("E:\\Spam", "spam", ".txt", 1)
I would like to know if this is good code? Is the approach a good one? Do you understand the code from the comments and the docstrings? Are there any bad practices you can spot Are there any improvements / better approaches to try?
Also I tested this script by manually adding the example script again and again how could this be better tested?
Please let me know what you think.