批量重命名文件名的一部分来自查找文件

3 投票
2 回答
2285 浏览
提问于 2025-04-17 14:33

编辑: 请看下面我的最终解决方案

我有一个大约12,700个文本文件的文件夹。

这些文件的名字像这样:

1 - Re/ Report Novenator public call for bury - by Lizbett on Thu, 10 Sep 2009.txt

文件名前面的数字是递增的,比如文件夹里的最后一个文件名是“12,700 - ”。

不幸的是,这些文件的顺序不是按时间排列的,而我需要它们按时间排序。幸运的是,我有一个单独的CSV文件,其中映射了ID号码,比如上面例子中的1实际上应该是25(因为它前面有24条消息),2应该是8,3应该是1,依此类推,像这样:

OLD_FILEID  TIMESORT_FILEID
21      0
23      1
24      2
25      3

我只需要更改文件名中的这个开头数字,其他的都不需要动。我的想法是,打开文件名,检查在破折号前面的数字,然后在CSV文件中查找,把它替换成对应的值,然后用调整后的标题保存文件,再继续下一个文件。

那么,做这个的最佳方法是什么呢?我对Python还是个新手,但玩过一段时间,觉得跟着大多数的指示或建议还是没问题的。谢谢 :)

e: 按照下面的指示尽量去做,我做了这个,但不管用,我也不太确定为什么:

import os
import csv
import sys

#open and store the csv file
with open('timesortmap.csv','rb') as csvfile:
timeReader = csv.reader(csvfile, delimiter = ',', quotechar='"')

#get the list of files
for filename in os.listdir('DiggOutput-TIMESORT/'):
oldID = filename.split(' - ')[0]
newFilename = filename.replace(oldID, timeReader[oldID],1)
os.rename(oldID, newFilename)

我得到的错误是:

TypeError: '_csv.reader' object is not subscriptable

我没有使用DictReader,但这是因为当我使用csv.reader并打印行时,它看起来像这样:

['12740', '12738']
['12742', '12739']
['12738', '12740']
['12737', '12741']
['12739', '12742']

而当我使用DictReader时,它看起来像这样:

{'FILEID-TS': '12738', 'FILEID-OLD': '12740'}
{'FILEID-TS': '12739', 'FILEID-OLD': '12742'}
{'FILEID-TS': '12740', 'FILEID-OLD': '12738'}
{'FILEID-TS': '12741', 'FILEID-OLD': '12737'}
{'FILEID-TS': '12742', 'FILEID-OLD': '12739'}

然后我在终端得到了这个错误:

File "TimeSorter.py", line 16, in <module>
newFilename = filename.replace(oldID, timeReader[oldID],1)
AttributeError: DictReader instance has no attribute '__getitem__'

2 个回答

1

在Python中,这个操作其实非常简单,只需要用到csvos这两个模块。

Python有一个内置的字典类型,叫做dict,可以用来在处理过程中把csv文件的内容存储在内存中。简单来说,你需要用csv模块读取csv文件,把每一条记录转换成字典中的一项,通常用OLD_FILEID字段作为键,用TIMESORT_FILEID作为值。

接着,你可以用os.listdir()来获取文件列表,然后用循环一个一个地处理每个文件名。如果你需要过滤掉某些文件,可以看看glob模块。在循环中,你只需要提取与文件相关的数字,可以用类似下面的方式来实现:

file_number = filename.split(' - ')[0] 

然后调用os.rename(),传入旧文件名和新文件名。新文件名可以用类似下面的方式找到:

new_filename = filename.replace(file_number, file_mapping[file_number], 1)

这里的file_mapping就是从csv文件创建的字典。这样会把file_number的第一次出现替换为你映射文件中的数字。

编辑

正如TheodrosZelleke所指出的,按照我上面说的做,有可能会覆盖掉已有的文件。这里有几种可能的解决方案:

  1. 使用os.rename()把重命名后的文件移动到不同的目录(比如当前目录的子目录,或者更好的是,使用tempfile.mkdtemp()创建一个临时目录)。等所有文件都重命名完后,再用os.rename把文件从临时目录移动到当前目录。
  2. 给新文件名加个扩展名,比如.tmp,前提是这个扩展名不会引起其他冲突。等所有重命名完成后,再用第二个循环把文件名中的.tmp扩展名去掉。
1

这是我和朋友们一起研究出来的,如果有人找到并想了解这个内容的话:

import os
import csv
import sys

IDs = {}

#open and store the csv file
with open('timesortmap.csv','rb') as csvfile:
        timeReader = csv.reader(csvfile, delimiter = ',', quotechar='"')

        # build a dictionary with the associated IDs
        for row in timeReader:
              IDs[ row[0] ] = row[1]

# #get the list of files
path = 'DiggOutput-OLDID/'
tmpPath = 'DiggOutput-TIMESORT/'
for filename in os.listdir('DiggOutput-OLDID/'):
    oldID = filename.split(' - ')[0]
    newFilename = filename.replace(oldID, IDs[oldID])
    os.rename(path + filename, tmpPath + newFilename)

撰写回答