Python使用正则表达式重命名文件

3

你好,我想将一个源模式的文件(例如IMG_20190401_235959.jpg)重命名为目标模式的文件(例如2019-04-01_23_59_59.jpg)。

我尝试在Python中完成这个任务,但是我不知道如何使用正则表达式来构建新的文件名:

"最初的回答"

你可以使用re模块来处理正则表达式。首先,你需要从原始文件名中提取日期和时间信息。接下来,你可以使用strftime()函数来将日期和时间格式化为所需的目标模式。最后,你可以使用os.rename()函数来重命名文件。

#!/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

1
你真的需要正则表达式吗?我相信大多数用户可以很快为您提供一个非正则表达式的好解决方案。 - KuboMD
1
如果所有文件的长度都完全相同,为什么要使用正则表达式?一个我能想到的解决方案是将文件名 .split(''),添加所需字符,然后重新连接。 - 101arrowz
4
你为什么要将它向前推迟3天?(或者这是一个错误吗?20190401 -> 2019-04-04) - Nir Alfasi
你可以先通过下划线将文件名(不包括扩展名)分割成三个部分,第一个部分可以丢弃,然后解析其他两个部分。我在这里看不出正则表达式的任何优势,你可以将这些字段解析为日期/时间或字符串。 - Nir Alfasi
@alfasin 是的,这是一个错误。 - MaD
是的,我想要一个正则表达式,因为源模式和目标模式可能与我提供的不同。在这种情况下,我认为正则表达式更合适,对吧? - MaD
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

1
你可以使用 re.findall 从文件路径中抓取必要的组,并重新连接:
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'))

输出:

'2019-04-01_23_59_59.jpg'

然后:

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)

1

不确定正则表达式是否是最佳选择。您可以将其拆分并使用基本的字符串操作。

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)

我喜欢这种方法。简单而高效。 - SanV

1

您可以在使用正则表达式之前进行预编译。 您可以按照以下方式进行操作:

import re

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

这里的sub_name是一个函数,你可以在后面的for循环中使用它来替换每个图像的名称。
注意:忽略大小写在Windows下可能很有用,但你也需要适应对glob.glob的调用。
以下是使用glob.glob的解决方案,但你也可以使用os.walk来浏览目录,搜索所有图像......
# 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)

我注意到您使用了print语句和Python 2.6的异常语法。更好的做法是使用新的语法。 如果您使用的是Python 2.7,您可以添加以下指令:
from __future__ import print_function

将它放在您的导入顶部...

0
以下代码对我有效,但它仅使用正则表达式来删除文件名中的IMG_,因此您也可以完全放弃正则表达式。
newfilename = re.sub('IMG_', '', filename)
newfilename = newfilename[0:4] + '-' + newfilename[4:6] + '-' + newfilename[6:11] + '_' + newfilename[11:13] + '_' + newfilename[13:]

为什么不直接使用 newfilename = newfilename[4:] 而不是使用 re.sub 呢?这样就完全不需要用到正则表达式了。 - 101arrowz
是的,这就是我在注释中所指的“...所以你也可以完全放弃正则表达式”的意思。 - schilli

0
如果您的输入是一致的,那么这应该可以工作:
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接