为什么我的循环在第一次迭代时可以正常工作,但在我正在循环的整个循环中却不能?

Why does my loop work correctly on the first iteration but not on the full set I'm looping through?

我正在尝试根据其中包含的 folders/files 批量重命名文件夹(然后将每个路径末尾的图像文件移动到它们各自的 model/color 文件夹目录中)。 每个 folder/file 都有类似的命名约定 MODEL_COLOR。

下面的代码有效,但似乎只在第一个文件夹上正常工作,换句话说,文件夹被正确重命名,但代码的最后一段似乎是在包含图像和移动的文件夹中它到相应的路径,而不是专门将图像移动到相应的路径并删除它们原来所在的文件夹。

在循环迭代的第一个文件夹中,它实际上将图像移动到正确的 Model > Color 目录,但在之后的所有文件夹中,它似乎正在移动 文件夹 包含将图像放入正确的模型 > 颜色目录中,而不是将图像单独移动到相应的目录中

在查看论坛后,我看到了类似的问题,当更改目录或删除某些实例时,循环无法正确迭代,因为在循环过程中初始设置发生变化(即删除或重命名部分迭代时的路径)。我很确定这是一个简单的修复,但我似乎找不到最有效的解决方案。

标准文件夹名称:

import glob, os, shutil

folder = 'C:\testing'
# create new folder directory based on Model/Color [is working, but moves file_path into base directory]

# arr = []    
for file_path in glob.glob(os.path.join(folder, '*_*')):
    new_dir = file_path.rpartition('_')[0]
    new_subdir = file_path.rpartition('_')[2]
    try:
        os.mkdir(os.path.join(new_dir, new_subdir))
    except WindowsError:
        # Handle the case where the target dir already exist.
        pass
    shutil.move(file_path, os.path.join(new_dir, new_subdir))
    # arr.append(file_path)

您看不到实际错误的原因是您在 except: 语句中发现了太多错误。

你打算抓FileExistsError,所以你也应该只找这个。否则您会注意到代码实际上抛出 FileNotFoundError.

原因是 os.mkdir 不会 自动创建父目录。它只创建一层深的目录,但您的代码需要两层新目录。

为此,您必须改用 os.makedirs(...)。方便地,os.makedirs 还接受一个 exist_ok 标志,它摆脱了整个 try:-except: 构造。

进一步说明,您的代码中有很多重复计算:

  • file_path.rpartition('_') 被计算两次
  • os.path.join(new_dir, new_subdir) 被计算两次

我建议将它们存储在有意义的变量中。这会加速您的代码,使其更具可读性和可维护性。

这是您的代码的修改版本:

import glob
import os
import shutil

folder = 'C:\testing'

for file_path in glob.glob(os.path.join(folder, '*_*')):
    (new_dir, _, new_subdir) = file_path.rpartition('_')
    target_path = os.path.join(new_dir, new_subdir)

    os.makedirs(target_path, exist_ok=True)

    shutil.move(file_path, target_path)

进一步improvements/fixes

你的代码中还有一堆错误:

  • 你不检查 glob 找到的东西是否是一个文件
  • _ 拆分不会在目录分隔符处停止。这意味着 C:\my_files\bla 之类的内容将拆分为 C:\myfiles\bla.

我认为您不关心其中任何一个,因为您认为 'the user would not use the script like this'。但这实际上是发生的情况:

  • 您有一个文件 C:\my_files\CL4003IN_45F,它将按预期移动到 C:\my_files\CL4003IN\45F\CL4003IN_45F
  • 你再运行脚本。该脚本将找到 C:\my_files\CL4003IN。它不检查它是否是一个文件夹,所以它无论如何都会处理它。然后它会将其拆分为 C:\myfiles\CL4003IN.
  • 整个文件夹 C:\my_files\CL4003IN 将移动到 C:\my\files\CL4003IN。因此原始文件 CL4003IN_45F 最终在 C:\my\files\CL4003IN\CL4003IN\45F\CL4003IN_45F

解决方法是:

  • 只在文件名上使用rpartition,而不是整个路径
  • 检查它实际上是文件还是目录

使用 pathlib 可以更轻松地解决这些任务中的大部分。我冒昧地重写了您的代码并解决了这些问题:

from pathlib import Path

folder = Path('C:\testing')

for file_path in folder.iterdir():
    file_name = file_path.name

    # Ignore directories
    if not file_path.is_file():
        continue

    # Split the filename by '_'
    (target_dir, _, target_subdir) = file_name.rpartition('_')

    # If no '_' found, ignore
    if not target_dir:
        continue

    # Compute the target path and create it if necessary
    target_path = folder / target_dir / target_subdir
    target_path.mkdir(parents=True, exist_ok=True)

    # Move the file to its new position
    target_file_path = target_path / file_name
    file_path.rename(target_file_path)

最后一点:folder.iterdir() 实际上 return 一个迭代器。但这在这种情况下应该不是问题,因为我们明确检查路径是否是现有文件而不是目录或已被删除的内容。但是如果你想 100% 安全写 list(folder.iterdir()).

通过将 glob 存储在 list 中来完成循环之前的迭代有助于避免一些不需要的错误。

#...
for file_path in list(glob.glob(os.path.join(folder, '*_*')
...#

但是通过修改我的代码并从循环中删除以下内容:

try:
    os.mkdir(os.path.join(new_dir, new_subdir))
except WindowsError:
    pass

允许代码遍历目录中的所有文件夹,而不将文件之前的文件夹转移到 new_dir > new_subdir 目录中。

适用于目录中多个文件夹的新代码是:

import glob, os, shutil

folder = 'C:\testing'

# create new folder directory based on Model > Color

for file_path in list(glob.glob(os.path.join(folder, '*_*'), recursive=True)):
    new_dir = file_path.rpartition('_')[0]
    new_subdir = file_path.rpartition('_')[2]
    shutil.move(file_path, os.path.join(new_dir, new_subdir))

这可能不是最有效的代码(并且可能无法在所有情况下工作,这还有待确定!),但目前肯定可以按预期工作。

特别感谢那些提供建议的人。