2
\$\begingroup\$

Let's say I have this folder tree:

├──FolderParent   
  ├── FolderA   
      ├── FolderAA_IMG             -- should be renamed
          ├── file_IMG_AA1.txt     -- should NOT be renamed
          ├── file_VID_AA1.txt     -- should NOT be renamed
      ├── file_IMG_A1.txt          -- should be renamed
      ├── file_PANO_A1.txt         -- should be renamed
  ├── FolderB   
      ├── file_IMG_B1.txt          -- should be renamed
      ├── file_PANO_B1.txt         -- should be renamed

As you can see, only the files/folders in the first children folder should be renamed, not the other ones. I follow this base code and then I added a second loop above but I wonder if the double for loop is the right way to go.

import os

# custom var
path= r"C:\Users\user\FolderParent"

# other cvar
number_modified_files= 0

# get all path of subfolder
all_subfolders = [f.path for f in os.scandir(path) if f.is_dir() ] 
print(all_subfolders)

for folder in all_subfolders: 
    # set the path to the folder: otherwise the rename file won't work 
    os.chdir(folder)
    #won't rename files in subfolder but will rename folder in the path 
    for filename in os.listdir(folder):
        print("new file:", filename)
        if "IMG_" in filename:
            os.rename(filename, filename.replace('IMG_', ''))
            number_modified_files +=1
        elif "PANO_" in filename:
            os.rename(filename, filename.replace('PANO_', ''))
            number_modified_files +=1
        elif "VID_" in filename:
            os.rename(filename, filename.replace('VID_', ''))   
            number_modified_files +=1


print(f"End : {number_modified_file} files renamed")
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

You should rethink your solution in terms of regexes:

#!/usr/bin/env python3

import re
from os import scandir, rename, path


def rename_children(parent):
    n_renamed = 0
    re_fname = re.compile('(IMG|PANO|VID)_')

    for child_dir in scandir(parent):
        if child_dir.is_dir():
            for child in scandir(child_dir):
                renamed = re_fname.sub('', child.name)
                if renamed != child.name:
                    new_path = path.join(child_dir.path, renamed)
                    print(f'Renaming {child.path} to {new_path}')
                    rename(child.path, new_path)
                    n_renamed += 1
    print(f'{n_renamed} files renamed')

Note the following changes:

  • Only one if to check whether the regex matches
  • Use scandir instead of listdir
  • Do not call chdir; there's no point
  • Don't call replace; the pattern check and the replacement operation can be combined by using sub
  • Don't store a list of all_subfolders; simply iterate over the results
\$\endgroup\$
2
\$\begingroup\$

Use regex to replace pattern in filename

The structure of these if else all similar, you can use re to simplify it.

if "IMG_" in filename:
    os.rename(filename, filename.replace('IMG_', ''))
    number_modified_files +=1
elif "PANO_" in filename:
    os.rename(filename, filename.replace('PANO_', ''))
    number_modified_files +=1
elif "VID_" in filename:
    os.rename(filename, filename.replace('VID_', ''))   
    number_modified_files +=1

So you are looking for IMG_, PANO_ and VID_ in filename and try to replace it delete this part.

Instead of using os.rename multiply times, we can use re.sub(pattern, repl, string, count=0, flags=0) to do this.

It will Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl.

pattern = 'IMG_|PANO_|VID_'
renamed_filename = re.sub(pattern, '', filename)

The pattern meaning match one in three. I am not sure if your are familiar with regex, here is the doc.

And if the renamed_filename not equal filename it is modified, so whole part will be

pattern = 'IMG_|PANO_|VID_'
renamed_filename = re.sub(pattern, '', filename)
if renamed_filename != filename:
    number_modified_files +=1
    os.rename(filename, renamed_filename)

Edit: Incorrect to use re.sub with os.rename

To fix this just remove the os.chdir(folder), there is no point doing this

# os.chdir(folder)
...
pattern = 'IMG_|PANO_|VID_'
renamed_filename = re.sub(pattern, '', filename)
file_path = os.path.join(folder, filename)
if renamed_filename != filename:
    number_modified_files +=1
    renamed_file_path = os.path.join(folder, renamed_filename)
    os.rename(file_path, renamed_file_path)

Regex side effect

But the regex code will work differ from your original code, as in your code, the replace end if it match in one pattern, but regex solution will try to replace all patterns in IMG_ PANO_ and VID_.

Store replace pattern in list

I suggest you use a list to store the patterns(IMG_ PANO_ and VID_)

if you wanna stop replace in the first match, use a loop to check one by one,

patterns = ["IMG_", "PANO_", "VID_"]
...
for pattern in patterns:
    if pattern in filename:
        os.rename(filename, filename.replace(pattern, ''))
        number_modified_files +=1

Or if you wanna replace all patterns, use regex

re.compile("|".join(patterns))

It is easy for only 3 patterns now, but will drive you crazy if there are 30.

\$\endgroup\$
5
  • \$\begingroup\$ There's no point to passing the count and flags kwargs to re.sub in this case. \$\endgroup\$
    – Reinderien
    Commented Dec 28, 2018 at 15:19
  • \$\begingroup\$ Also, don't use the re.sub form at all. Use re.compile and call sub on the compiled regex object. \$\endgroup\$
    – Reinderien
    Commented Dec 28, 2018 at 15:20
  • \$\begingroup\$ Finally: you're applying rename and sub on the same filename, which is incorrect. sub must only be applied on the filename without path, and rename must have the path. \$\endgroup\$
    – Reinderien
    Commented Dec 28, 2018 at 15:21
  • \$\begingroup\$ you are correct for "sub must only be applied on the filename without path", I took a mistake on it, and "There's no point to passing the count and flags kwargs to re.sub in this case. " I just showing the function, and thanks for " Use re.compile and call sub on the compiled regex object.", updating my comment \$\endgroup\$ Commented Dec 28, 2018 at 15:31
  • \$\begingroup\$ Looks OK now :) \$\endgroup\$
    – Reinderien
    Commented Dec 28, 2018 at 22:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.