【问题标题】:Renaming folders and files while os.walk()ing them missed some files after change of the directory nameos.walk() 重命名文件夹和文件在更改目录名称后丢失了一些文件
【发布时间】:2018-11-02 18:16:21
【问题描述】:

我有一个这样的文件夹结构:

Template
  - Template1
  - Template2
TemplateTest
  - TemplateTest1
Config
  - TemplateConfig

我想将每个文件名和每个文件夹名的“模板”替换为“MyApp”。

这是我的代码:

for root, dirs, files in os.walk(path):
    for name in files:
        if name.startswith("Template"):
            replace = name.replace("Template",'MyApp')
            os.rename(os.path.join(root,name),os.path.join(root,name.replace(old,new)))
    for name in dirs:
        if name.startswith("Template"):
            replace = name.replace("Template",'MyApp')
            os.rename(os.path.join(root,name),os.path.join(root,replace))

奇怪的是,这只替换了文件夹名称,而父文件夹名称的文件名不需要更改。像这样:

MyApp
  - Template1
  - Template2
MyAppTest
  - TemplateTest1
Config
  - MyAppConfig

但如果我执行此代码两次,那么它将替换文件。 我想知道为什么以及如何更改代码以替换我需要的所有内容?

【问题讨论】:

  • @PatrickArtner 手动输入的文件夹名称,抱歉,它们应该是相同的。
  • 您可以尝试先只重命名目录,然后使用for root, dirs, files in os.walk(path): TWICE 重命名文件
  • @Bazingaa 是的,我已经发现我需要执行两次才能得到我想要的。我很好奇为什么,有什么办法可以一次改变它。
  • oldnew 是什么?没有给出。
  • 您使用的是什么平台?这在 Windows 10 上对我来说效果很好......在我将 old,new 替换为 os.rename(os.path.join(root,name),os.path.join(root,replace))

标签: python os.walk


【解决方案1】:

(请注意,os.walk 的调用签名是:

os.walk = walk(top, topdown=True, onerror=None, followlinks=False)

所以你正在传递 TrueNoneFalse。)

问题与os.walk 遍历目录和文件的顺序以及它进入的目录和文件有关。

特别是,它首先读取位于path 的目录。这会产生以下结果:

['Template', 'TemplateTest', 'Config']

所有这些都是目录,所以它下次要走的子目录列表是一样的,并且没有文件。这在第一次迭代中作为三个值返回:

path
['Template', 'TemplateTest', 'Config']
[]

然后您编写自己的代码,在其中调用 os.renameTemplate,这样它现在被命名为 MyApp,在 TemplateTest 上,所以目录现在被命名为 MyAppTest

接下来,os.walk 代码尝试读取子目录 Template。这失败了,所以什么也没有发生(onerrorNone)。

接下来,os.walk 代码尝试读取子目录 TemplateTest。这失败了,所以什么也没有发生。

最后,os.walk 代码尝试读取子目录Config。这成功了,一切顺利。

有两种不同的解决方案:您可以将topdown 设置为False,或者您可以更新名为dirs 的列表,以便os.walk 知道目录的 名称。 (编辑:我不确定topdown=False 会修复它;这需要测试。)

(编辑:topdown=False 真的会修复它。这在文档中有描述:

当 topdown 为 True 时,调用者可以就地修改 dirnames 列表(可能使用 del 或 slice 赋值),并且 walk() 只会递归到名称保留在 dirnames 中的子目录;这可用于修剪搜索,强制执行特定的访问顺序,甚至在调用者再次恢复 walk() 之前通知 walk() 有关调用者创建或重命名的目录。当 topdown 为 False 时修改 dirnames 对 walk 的行为没有影响,因为在自底向上模式下,dirnames 中的目录是在 dirpath 本身生成之前生成的。

)

【讨论】:

    【解决方案2】:

    如果有疑问 - print 它:

    创建数据结构:

    import os
    
    
    for d in ["./Template","./TemplateTest","./Config"]:
        os.mkdir(d)
    
    for f in ["./Template/Template1.txt","./Template/Template2.txt",
              "./TemplateTest/TemplateTest1.txt", "./Config/TemplateConfig.txt"]:
        with open(f,"w") as f:
            f.write(" ")
    

    测试os.walk:

    for root, dirs, files in os.walk("./"): # no topdown means == True
        for name in files:
            if name.startswith("Template"):
                replace = name.replace("Template",'MyApp')
                print("renaming: ", os.path.join(root,name), " to ", os.path.join(root,replace))
                # os.rename(os.path.join(root,name),os.path.join(root,replace))
        for name in dirs:
            if name.startswith("Template"):
                replace = name.replace("Template",'MyApp')
                print("renaming: ", os.path.join(root,name), " to ", os.path.join(root,replace))
                # os.rename(os.path.join(root,name),os.path.join(root,replace))    
    

    如果您注释掉 for ... loops 并仅注释掉 print(root,dirs,files),则输出:

    ./             ['Config', 'Template', 'TemplateTest'] ['main.py']
    ./Config       []                                     ['TemplateConfig.txt']
    ./Template     []                                     ['Template1.txt', 'Template2.txt']
    ./TemplateTest []                                     ['TemplateTest1.txt']
    

    如果您再次注释 for 循环并将 rename 替换为 print,您会得到:

    renaming:  ./Template  to  ./MyApp            # aha - works
    renaming:  ./TemplateTest  to  ./MyAppTest    # aha - works 
    renaming:  ./Config/TemplateConfig.txt  to  ./Config/MyAppConfig.txt   # works
    renaming:  ./Template/Template1.txt  to  ./Template/MyApp1.txt       # folder not updated
    renaming:  ./Template/Template2.txt  to  ./Template/MyApp2.txt       # folder also not updated
    renaming:  ./TemplateTest/TemplateTest1.txt  to  ./TemplateTest/MyAppTest1.txt  # also not updated
    

    如果您查看文档,它可能会说迭代 os.walk() 生成的结果时发生的更改不会反映在生成的数据中。

    您本质上是“在迭代时更改可交互对象”;o)

    来自链接的独库:

    topdownTrue 时,调用者可以就地修改目录名列表(可能使用del 或切片赋值),walk() 只会递归到名称保留在目录名中的子目录;这可用于修剪搜索,强制执行特定的访问顺序,甚至在调用者再次恢复 walk() 之前通知 walk() 有关调用者创建或重命名的目录。 当topdownFalse 时修改目录名对walk 的行为没有影响,因为在自底向上模式下,目录名中的目录是在目录路径本身生成之前生成的。

    【讨论】:

    • :-D 谢谢。我想我只需要运行它两次。
    猜你喜欢
    • 1970-01-01
    • 2019-03-05
    • 1970-01-01
    • 1970-01-01
    • 2012-01-18
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 2020-01-15
    相关资源
    最近更新 更多