3

Hi I want to rename files with one source pattern (for example IMG_20190401_235959.jpg) to a destination pattern (for example 2019-04-01_23_59_59.jpg)

I'm trying to do that in python but i can't find out how to use a regex to build the new filename :

#!/usr/bin/python

import os, glob, sys, re    
os.chdir(sys.argv[1])
for filename in glob.glob("IMG_*.jpg"):
    newfilename = re.sub(?????
    try:
       os.rename(filename,newfilename)
    except OSError,e:
       print e
6
  • 1
    Do you actually need a regex? I'm sure most users could provide you with a good non-regex solution very quickly. Commented Apr 25, 2019 at 18:57
  • 1
    If all of the files have the exact same length, why use RegEx? A solution off the top of my head would be to .split('') the filename, add the characters you need, and rejoin. Commented Apr 25, 2019 at 18:57
  • 4
    Why do you want to shift it 3 days forward? (or is it a mistake? 20190401 -> 2019-04-04) Commented Apr 25, 2019 at 18:57
  • You can start by splitting the file-name (without ext) by underscore, then you'll have three parts, the first you can discard and parse the other two parts. I don't see any advantage for regex here, you can parse these fields either as date/time or as strings. Commented Apr 25, 2019 at 19:00
  • @alfasin yes it is a mistake. Commented Apr 25, 2019 at 20:54

6 Answers 6

6
import re

regex = re.compile(r'^IMG_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})\.jpeg$')

oldStr = 'IMG_20190401_235959.jpeg';

match = regex.match(oldStr)

newStr = '{}-{}-{}_{}_{}.jpg'.format(*match.groups())

print(newStr) # 2019-04-01_23_59.jpg
Sign up to request clarification or add additional context in comments.

Comments

1

What you can do is to precompile the RegEx before using it. You can proceed that way:

import re

sub_name = re.compile(r"IMG_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})", flags=re.I).sub

Here, sub_name is a function that you can use later in you for loop to replace the name of each image.

Note: Ignoring the case (upper/lower-case) can be useful under Windows, but you also need to adapt the call to glob.glob.

Below is a solution which use glob.glob but you can also use os.walk to browse a directory, searching all images…

# coding: utf-8
import glob
import os
import re
import sys

sub_name = re.compile(r"IMG_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})", flags=re.I).sub

work_dir = sys.argv[1]

for old_path in glob.glob(os.path.join(work_dir, "IMG_*.jpg")):
    dirname, old_name = os.path.split(old_path)
    new_name = sub_name("\\1-\\2-\\3_\\4_\\5_\\6", old_name)
    new_path = os.path.join(dirname, new_name)
    try:
        os.rename(old_path, new_path)
    except OSError as exc:
        print(exc)

I noticed that you use the print statement, and the Python 2.6 syntax for exception. It is preferable to use the new syntax. If you use Python 2.7, you can add the directive:

from __future__ import print_function

Put it at the top of your imports…

Comments

1

You can use re.findall to grab the necessary groups from the file path, and the rejoin:

import re
def new_path(s):
  _, a, b, f_type = re.findall('[a-zA-Z0-9]+', s)
  new_b = '_'.join(b[i:i+2] for i in range(0, len(b), 2))
  return f'{a[:4]}-{a[4:6]}-{a[6:]}_{new_b}.{f_type}'

print(new_path('IMG_20190401_235959.jpg'))

Output:

'2019-04-01_23_59_59.jpg'

Then:

import os, glob, sys, re    
os.chdir(sys.argv[1])
for filename in glob.glob("IMG_*.jpg"):
  try:
    os.rename(filename, new_path(filename))
  except OSError,e:
    print(e)

Comments

1

Not sure if regex is the best option here. You can split it up and use basic string operations pretty easily:

original = 'IMG_20190401_235959.jpg'
ol = original.split('_')
date = f'{ol[1][:4]}-{ol[1][4:6]}-{ol[1][6:8]}'
time = f'{ol[2][:2]}_{ol[2][2:4]}_{ol[2][4:6]}'
new = f'{date}_{time}.jpg'
print(new)

1 Comment

I like this approach. simple and efficient.
0

If your input is consistent, this should work:

import re
pattern = r"IMG_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})"
test_str = "IMG_20190401_235959.jpg"
subst = "\\1-\\2-\\3_\\4_\\5_\\6"
result = re.sub(pattern, subst, test_str, 0, re.MULTILINE)
if result:
    print (result)

# 2019-04-01_23_59_59.jpg

Comments

0

The following code worked for me, however it only uses regex to remove the IMG_ in the filename, so you can also just drop the regex completely.

newfilename = re.sub('IMG_', '', filename)
newfilename = newfilename[0:4] + '-' + newfilename[4:6] + '-' + newfilename[6:11] + '_' + newfilename[11:13] + '_' + newfilename[13:]

2 Comments

Instead of using RegEx at all, why not just do newfilename = newfilename[4:] instead of the re.sub?
Ya, that's what I mean with the comment "...so you can also just drop the regex completely."

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.