Search code examples
pythonenumerate

Add numbering to file name


I'm a Python newbie. I have a directory with hundreds of files. I would like to add a progressive number at the beginning of each file, for example: 001_filename and so on ... I would like to use three digits for the numbering. Thanks to those who can help me!


Solution

  • The following Python script renames all the files in a given directory.

    Explanation

    It first checks whether the path provided is actually a directory and if it exists. If not it prints an error message and leaves the program with a non-zero exit code.

    Otherwise, it retrieves all the filenames in that directory and sorts the files according to their last modified date. You can easily adjust the sorting criteria by providing another lambda function for key= argument of sorted(). This is not actually required but as os.listdir() gets the result in an order that is determined by your filesystem this is the only way to make sure there are renamed according to a criteria you specify.

    After that it determines the number of digits required to uniquely identify each file with an unique ID. This will make sure the script works even if there are more than hundreds of files in the directory (like in your case). To adhere to your question though using max() I make sure it will always be at least three digits, even if they would not be required to uniquely identify a file.
    It then finally renames the file if that is necessary i.e. the file does not already have the desired filename.

    Script

    import os
    import sys
    
    dir = "mydir"
    if not (os.path.exists(dir) and os.path.isdir(dir)):
        # write error message to stderr
        print(f"Directory {dir} does not not exist or is not a directory.", file=sys.stderr)
        # exit program with exit code 1 indicating the script has failed
        sys.exit(1)
    # get all files in the directory and store for each file it's name and the full path to the file
    # This way we won't have to create the full path many times
    my_files = [(file, os.path.join(dir, file)) for file in os.listdir(dir) if os.path.isfile(os.path.join(dir, file))]
    # sort by "modified" timestamp in reverse order => file with most recent modified date first
    # we need to use the fullpath here which is the second element in the tuple
    sorted_by_creation_date = sorted(my_files, key=lambda file: os.path.getmtime(file[1]), reverse=True)
    # get number of digits required to assign a unique value
    number_of_digits = len(str(len(my_files)))
    # use at least 3 digits, even if that's not actually required to uniquely assign values
    number_of_digits = max(3, number_of_digits)
    
    # loop over all files and rename them
    print("Renaming files...")
    for index, (file, fullpath) in enumerate(my_files):
        # rename files with leading zeros and start with index 1 instead of 0
        new_filename = f"file{index + 1}.txt"#f"{index + 1:0{number_of_digits}d}_{file}" #f"file{index + 1}.txt"
        if new_filename == file:
            # move on to next file if the file already has the desired filename
            print(f"File already has the desired filename {file}. Skipping file.")
            continue
        # concatenate new filename with path to directory
        new_name = os.path.join(dir, new_filename)
        # rename the file
        print(f"{file} => {new_filename}")
        os.rename(fullpath, new_name)
    
    print("Done.")
    

    If you want to run the script in a certain directory using python myscript.py and rename the files in that directory you can use dir = os.getcwd() to get the current working directory in your script instead of hardcoding the dir like in the script above (dir = "mydir").

    This implementation does not rename files in nested directories, but you could adjust the program to do that if required. Probably want to have a look at os.walk() in that case.

    Example

    Example for a directory like this:

    Note: this uses the script from above with the hard-coded directory name mydir.

    mydir/
    ├── file1.txt
    ├── file2.txt
    ├── file3.txt
    └── Test
        └── test.txt
    

    Run the script

    python myscript.py
    

    Script output

    Renaming files...
    file1.txt => 001_file1.txt
    file2.txt => 002_file2.txt
    file3.txt => 003_file3.txt
    Done.
    

    Result

    mydir/
    ├── 001_file1.txt
    ├── 002_file2.txt
    ├── 003_file3.txt
    └── Test
        └── test.txt
    

    As you can see the files are renamed as expected and nested directories remain untouched.