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

本文属于利用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之类的文件,就表示该目录下的文件需要重命名。重命名的方法是首先取出该目录的名称,然后添加到目录下面的每个文件名的前面。

 

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

  1. 遍历指定顶层目录下面的各个最底层的目录;
  2. 获取该最底层目录下的文件列表;
  3. 如果文件名是01.mp3样式的名称,则需要重命名;
  4. 否则,则说明已经手工修改过了,直接跳过即可;
  5. 重命名时,
  • 首先获取最底层目录的名称;
  • 依次构造每个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代码还是需要及时优化。

 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值