您的位置:首页 > 编程语言 > Python开发

Python入门教程-05 休闲一刻 文件重命名示例

2014-04-19 16:18 519 查看
本文属于利用Python解决日常小问题的一个例子。

1. 问题描述

书虫系列是非常优秀的英语学习读物,这个系列包括多个级别,如入门级1、入门级2、第三级,等等:



而每个级别下又包括多个故事:



每个故事的音频文件是01.mp3,02.mp3等:



如果把所有这些音频文件导入到iTune或Android手机的音频播放器中,则会出现多个诸如01.mp3文件,而无法得知每个01.mp3对应的具体的故事名称,所以在选曲的时候会非常不方便。



为此,希望把故事名称添加到01.mp3等文件名的前面,即如下的效果:



如果觉得文件名太长,可以把故事名称最前面的01、02等字样去掉。

2. 低效的方法

当然一种最直接的方法就是手工去重命名每个mp3文件。但因为有大量的文件,这个重命名的活就变得很枯燥,且效率低下。

为此,要寻求一种自动修改的方法,——毕竟这个重命名是有规律可循。

这里以Python级别为例来实现这个目的。

3. 解决方案

3.1 总体思路

可以遍历指定目录下面的各个文件夹,如果该文件夹下面是01.mp3之类的文件,就表示该目录下的文件需要重命名。重命名的方法是首先取出该目录的名称,然后添加到目录下面的每个文件名的前面。

为此,分为如下几个迭代:

遍历指定顶层目录下面的各个最底层的目录;
获取该最底层目录下的文件列表;
如果文件名是01.mp3样式的名称,则需要重命名;
否则,则说明已经手工修改过了,直接跳过即可;
重命名时,

首先获取最底层目录的名称;
依次构造每个mp3文件的目标文件名;
重命名文件;

接下来按照上述步骤,逐步丰富Python脚本。

Sprint1:遍历所有目录

在“Python语言获取目录下所有文件或目录的方法”一文中,给出了递归遍历某目录下所有子目录和文件的函数。基于这个函数,我们可以类似地设计这样的一个函数:

指定一个目录,返回这个目录下面的所有最底层目录。

和链接不同的是,这里仅仅获取目录,而不包括文件。——这样的话,就可以按照前面设计的思路一步步往下走。但是否一定要这样处理呢?

软件开发中,一个非常流行的词汇就是“变化”。就这里来讲,我们的设计也需要变化。更恰当地讲,我们这里还称不上“设计”,称谓“算法”可能更为合适。毕竟是一个小程序。

当外部可用资源发生变化的时候,及时调整已有的算法。

举个例子,假如我们已经有个库函数,可以返回指定目录下的所有最底层目录。那么我们使用最开始的算法就是合适的。

至此,Sprint1的结论是:调整设计,更新迭代计划。——Sprint 1更像一个Technical Sprint!

这里的调整主要是把原计划的第一步和第二步合并一起。

Sprint2:遍历所有的mp3文件

我们直接重用“Python语言获取目录下所有文件或目录的方法”,得到如下的代码:

# Findthe every dir, if 01.rm exist in it, then rename it.
#!/usr/bin/python

'''
Utilitiesof file & directories.
'''

import os

# Get theall files & directories in the specified directory (path).
defget_recursive_file_list(path):
current_files = os.listdir(path)
all_files = []
for file_name in current_files:
full_file_name = os.path.join(path,file_name)
all_files.append(full_file_name)

if os.path.isdir(full_file_name):
next_level_files =get_recursive_file_list(full_file_name)
all_files.extend(next_level_files)

return all_files

top_dir ="C:\\English\\书虫"
all_files= get_recursive_file_list(top_dir)
printall_files


运行结果如下:

C:\English\书虫>python rename.py
File "rename.py", line 24
SyntaxError:Non-ASCII character '\xca' in file rename.py on line 24, but no enc
odingdeclared; see http://www.python.org/peps/pep-0263.html for details


这个是编码的问题,因为第24行用到了中文。为此,代码修改如下(添加了第一行):

#coding=gbk

# Findthe every dir, if 01.rm exist in it, then rename it.
#!/usr/bin/python

'''
Utilitiesof file & directories.
'''

import os

# Get theall files & directories in the specified directory (path).
defget_recursive_file_list(path):
current_files = os.listdir(path)
all_files = []
for file_name in current_files:
full_file_name = os.path.join(path,file_name)
all_files.append(full_file_name)

if os.path.isdir(full_file_name):
next_level_files =get_recursive_file_list(full_file_name)
all_files.extend(next_level_files)

return all_files

top_dir ="C:\\English\\书虫"
all_files= get_recursive_file_list(top_dir)
for filein all_files:
print file.decode("gbk")


运行结果:



目前的代码是打印了目录和文件,所以需要进一步把目录过滤掉(添加了if语句):

top_dir = "C:\\English\\书虫"
all_files = get_recursive_file_list(top_dir)
for file in all_files:
if os.path.isfile(file):
print file.decode("gbk")


运行结果:



大家可能会注意到脚本文件rename.py也在输出之列。这个倒无关紧要,因为我们后面的算法只需要处理01.mp3之类的文件,会自动过滤掉其他文件。

至此,我们达到了sprint2的目标,即可以遍历指定目录下的所有mp3文件了。接下来就是重命名。

Sprint3:重命名mp3文件

有了文件名,重命名相对简单一些。为此,确定如下的伪代码:

def need_rename(filename):
return True

def rename(filename):
old_filename = filename
new_filename = filename # to be done
#rename it

top_dir = "C:\\English\\书虫"
all_files = get_recursive_file_list(top_dir)
for file in all_files:
if not need_rename(file):
continue
rename(file)
print file.decode("gbk")


注意到,我们已经对代码进行了重构,把对文件和目录的判别放到need_rename()函数中。接下来就是丰富上面两个桩函数。

通常来讲,对于每个函数,都需要单独编码、单独测试。但对我们这个小例子来讲,全部略过,即砍掉了UT,直接IT&ST了。进一步地,也没有做Automation Test,而全部Manual Test。

最后的代码:

def need_rename(filename):
if not os.path.isfile(filename):
return False

base_filename = os.path.basename(filename)
pattern = re.compile("^[\d]{2}.mp3$")
if pattern.match(base_filename):
print base_filename
return True
else:
return False

def rename(filename):
old_filename = filename

base_filename = os.path.basename(filename)
dir_name = os.path.dirname(filename)
#print "%s ----------- %s" % (dir_name, base_filename)

story_name = os.path.basename(dir_name)
#print "story name: ", story_name

new_filename = os.path.join(dir_name, story_name[3:] + " " + base_filename)
os.rename(old_filename, new_filename)
print "%s ==> %s" % (old_filename, new_filename)

top_dir = "C:\\English\\书虫"
all_files = get_recursive_file_list(top_dir)
for file in all_files:
if not need_rename(file):
continue
rename(file)


运行效果:



可以看到总共50行不到的代码,即实现了大量文件的重命名,从而把自己从枯燥无味的工作中解脱出来。

4. 后记

可以看到need_rename和rename两个函数存在一定的代码重复,而且一些重复操作也降低了代码运行效率。

作为小练习、小程序,这种重复、性能问题是可以容忍的。但通常在开发正式的程序时,对这种bad smell代码还是需要及时优化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: