【问题标题】:Renaming files according to a set of rules根据一组规则重命名文件
【发布时间】:2012-03-23 13:03:16
【问题描述】:

我正在使用(或尝试使用)python 脚本来(目前)删除和更改文件名

我希望在一个脚本中包含一堆重命名功能。

第一季度

我想知道该怎么做

第二季度

我还希望能够列出一个列表,这样我就可以将多个事物重命名为同一个名称,而不必为每个事物都设置一行: 现在应该是:

[os.rename(f, f.replace('B', 'A')) for f in os.listdir('.') if not f.startswith('.')]
[os.rename(f, f.replace('C', 'A')) for f in os.listdir('.') if not f.startswith('.')]
[os.rename(f, f.replace('D', 'A')) for f in os.listdir('.') if not f.startswith('.')]
[os.rename(f, f.replace('E', 'A')) for f in os.listdir('.') if not f.startswith('.')]
[os.rename(f, f.replace('F', 'A')) for f in os.listdir('.') if not f.startswith('.')]

我希望能够将其放入某种完整的“替换 B、C、D、E 和/或 F

第三季度

我的第三个问题是我怎样才能让它从文件名中删除所有“-”,除非其中的一部分是“可口可乐”(或其他东西)

目前我有几个蹩脚的解决方法。但它们效率不高

【问题讨论】:

    标签: python


    【解决方案1】:

    首先,您确定要使用 Python 执行此操作吗?有几个功能齐全的文件重命名实用程序可以满足您的需求。 (我个人最喜欢的是 Linux 下的 KRename。)

    假设您确实想在 python 中执行此操作...

    Q1) 将文件名与其扩展名分开

    使用os.path.splitext() 将文件名分为“名称”和“扩展名”部分。然后,您可以在不更改扩展名的情况下操作文件名,并在完成后将它们重新组合在一起。例如:

    import os, pprint
    filenames = [f for f in os.listdir('D:\\Freeware')]
    name_and_ext_list = [os.path.splitext(f) for f in filenames]
    pprint.pprint(filenames)
    pprint.pprint(name_and_ext_list)
    

    提供类似的输出

    ['a43.zip',
     'Amphetype-0.16-win32.exe',
     'aMSN-0.98.4-tcl85-windows-installer.exe',
     'andlinux-beta2-minimal.exe',
     'ATF-Cleaner.exe',
     'aurora-setup.exe',
    
    [('a43', '.zip'),
     ('Amphetype-0.16-win32', '.exe'),
     ('aMSN-0.98.4-tcl85-windows-installer', '.exe'),
     ('andlinux-beta2-minimal', '.exe'),
     ('ATF-Cleaner', '.exe'),
     ('aurora-setup', '.exe'),
    

    请注意,os.path.splitext() 比您可能自己滚动的任何东西都更强大。它不会被文件名中的多余点弄糊涂 - 例如:

    >>> os.path.splitext('Zipped Party Food Invoice 22.09.2011.xlsx.zip')
    ('Zipped Party Food Invoice 22.09.2011.xlsx', '.zip')
    

    Q2) “模糊”搜索和替换

    您的示例代码,将所有字符 BCDEF 替换为 A,可以使用 @kev 建议的正则表达式来完成。

    编辑 2 由于您想替换整个单词,而不是特定的单个字符 B, C, D, E, F,您可以尝试以下代码。这不是特别有效(它必须为您要搜索和替换的每个单词扫描一次文件列表) - 它有效,但欢迎改进。一个好的解决方案只需要遍历字符串。

    def replace_words ( input_string ):
        replacement_lists = { \
            "Electronics" : ["Computer", "CD Player", "Camera", "Coffee Grinder"],
            "Baked Goods" : ["Cheesecake", "Muffin","Cookie"] }
    
        output_string = input_string
        for type_of_thing, list_of_things in replacement_lists.iteritems():
            for thing in list_of_things:
                output_string = output_string.replace(thing, type_of_thing)
    
        return output_string
    
    input_names = [ \
    "Coffee Cup.jpg",
    "Computer Disks.docx",
    "Muffins.jar",
    "CD Player Maintenance.lzma",
    "Cookie Monster's 101 Types of Cookie.pdf" ]
    
    output_names = [replace_words(x) for x in input_names]
    

    输出如下:

    >>> pprint.pprint(input_names)
    ['Coffee Cup.jpg',
     'Computer Disks.docx',
     'Muffins.jar',
     'CD Player Maintenance.lzma',
     "Cookie Monster's 101 Types of Cookie.pdf"]
    >>> pprint.pprint(output_names)
    ['Coffee Cup.jpg',
     'Electronics Disks.docx',
     'Baked Goodss.jar',
     'Electronics Maintenance.lzma',
     "Baked Goods Monster's 101 Types of Baked Goods.pdf"]
    

    Q3) 删除所有破折号,除非被单词字符包围

    又是正则表达式的工作。试试:

    >>> teststring = "Coca-Cola - A History.pdf"
    >>> re.sub(r'(\W)-(\W)',r'\1\2',teststring)
    'Coca-Cola  A History.pdf'
    

    这将删除任何未被“单词字符”\w 包围的破折号,松散地定义为任何字母数字字符、任何数字或下划线。 \W 匹配 -word 字符,即任何 \w 匹配的内容。

    1) \w 取决于区域设置:如果您的文件名是俄语,那么西里尔字符也将被视为“单词字符”。

    注意事项:

    2) 正则表达式实际上匹配三个字符 - 替换字符串中的\1\2 用于将两个字符放回破折号的两侧。 (参见:“反向引用”。)

    3) 注意使用原始字符串 r"..." 而不是普通字符串 "..."。这是为了防止 Python 破坏正则表达式中的反斜杠。

    编辑:这是一个(未经测试的)示例,说明如何将文件名与扩展名分开处理。请注意,我已将所有繁重的工作转移到一个单独的函数中,而不是使用列表推导。

    列表推导式非常适合替换做一两件事的循环,但我个人发现嵌套列表推导式很难阅读。请记住,长度超过 80 个字符的行是代码异味的指标。

    import os, shutil, re
    def rename_file (original_filename):
        name, extension = os.path.splitext(original_filename)
        #remove one or more dashes, surrounded by non-word characters.
        modified_name = re.sub(r"(\W)-+(\W)",r"\1\2",name) 
        new_filename = modified_name + extension
        try:
            # moves files or directories (recursively)
            shutil.move(original_filename, new_filename)
        except shutil.Error:
            print ("Couldn't rename file %(original_filename)s!" % locals())
    
    target_dir = r"/home/trinity/nmap"
    targets = os.listdir(target_dir)
    [rename_file(f) for f in targets]
    

    【讨论】:

    • 哇,好棒的回应!!抱歉,我无法理解如何仅将重命名应用于名称,而不是文件扩展名,然后将其重新组合在一起。我假设我在开头插入一行告诉它:“[os.path.splitext(f) for f in filenames]”。但是,我该怎么做才能使重命名仅适用于文件名而不适用于扩展名?
    • @RobinHood:我添加了一个更具体的示例。请注意,我没有完成所需的全部错误/健全性检查 - 您可能想要检查 filename 是文件还是目录,它是否实际存在于磁盘上,等等。
    • 哦,它不是特殊目录之一 ... 或隐藏的“点文件”.ssh.minecraft 等(@kev 在他的回答但我总是忘记。)
    • 由于某种原因,我无法调整最后一个代码示例,让我用 os.path.split() 分割的任何内容的“根”部分做各种事情。一方面,我遇到了各种语法错误,其中 (original_filename) 没有被定义。我还希望将其应用于从中调用它的任何目录(无论我或用户所在的目录)。我如何将两者分开,然后有一些类别我可以列出替换列表(例如,用空格替换点,首字母大写,用空格替换“-”,除非它是“蓝光”的一部分,等等) ?
    • import os, re, pprint teststring = "Coca-Cola - A History.pdf" re.sub(r'(\W)-(\W)',r'\1\2' ,teststring) pprint.pprint(teststring) 还返回 'Coca-Cola - A History.pdf'...我觉得我做错了什么?
    【解决方案2】:
    import re
    [os.rename(f, re.sub(r'[B-F]', 'A', f)) for f in os.listdir('.') if not f.startswith('.')]
    

    【讨论】:

    • r'[B-F]' 是一个匹配B,C,D,E,F的正则表达式
    • 你可以做一个像r'(Camera|Computer|Coffee Grinder|Alarm Clock)'这样的正则表达式,它会匹配这些项目中的任何一个。管道符号 | 是正则表达式 alternation 运算符 - 将其视为逻辑 OR。
    • 编辑了我的答案Q2,以更好地反映您真正想要的 - 替换整个单词,而不是文字字符 A, B, C, D, E, F
    • 其实我只记得this substantially similar question。如果您想要最有效的解决方案,请参阅@tchrist 的回答。
    猜你喜欢
    • 2016-08-21
    • 2012-08-26
    • 2018-10-23
    • 2015-02-01
    • 2019-05-25
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 2019-06-11
    相关资源
    最近更新 更多