【问题标题】:Rearranging nested directory重新排列嵌套目录
【发布时间】:2021-07-23 01:03:32
【问题描述】:

我有一个结构如下的文件夹系统:

folderA 
- folder1
  - file1A.txt
- folder2
  - file2A.txt
- folder3
  - file3A.txt

folderB 
- folder1
  - file1B.txt 
- folder2 
  - file2B.txt 
- folder3
  - file3B.txt

我希望更改顺序,使字母文件夹上方的编号文件夹为:

folder1 
- folderA
  - file1A.txt
- folderB
  - file1B.txt

folder2 
- folderA
  - file2A.txt 
- folderB 
  - file2B.txt 

folder3 
- folderA
  - file3A.txt 
- folderB 
  - file3B.txt 

下面是一段代码,用于构建初始目录结构的MWE:

import os 
import shutil
import string

root_dir = os.getcwd()
os.chdir('/home/alletro/Tc-97/tools')
os.makedirs('master', exist_ok=True)
os.chdir('master') 
master_dir = os.getcwd()
top_tier = [f'folder{i}' for i in range(1,4)]
second_tier = [f'folder{i}' for i in list(string.ascii_uppercase)[:4]]
for folder in top_tier: 
    os.chdir(master_dir)
    os.makedirs(folder, exist_ok=True) 
    os.chdir(folder)
    fold_dir = os.getcwd()
    for sub_folder in second_tier:
        os.chdir(fold_dir)
        os.makedirs(sub_folder, exist_ok=True) 
        os.chdir(sub_folder)
        os.mknod("newfile.txt")
os.chdir(root_dir)

我找到了一个解决方案,可以让我获得目录树的字典:

def get_directory_structure(rootdir):
    """
    Creates a nested dictionary that represents the folder structure of rootdir
    """
    dir = {}
    rootdir = rootdir.rstrip(os.sep)
    start = rootdir.rfind(os.sep) + 1
    for path, dirs, files in os.walk(rootdir):
        folders = path[start:].split(os.sep)
        subdir = dict.fromkeys(files)
        parent = reduce(dict.get, folders[:-1], dir)
        parent[folders[-1]] = subdir
    return dir

然而,我正在努力寻找从这里到哪里去。

【问题讨论】:

    标签: python directory shutil python-os


    【解决方案1】:

    以下内容对我有用。有四个部分:

    1. 获取所有文件路径的列表。
    2. 使用该列表创建所需子文件夹的列表。
    3. 创建子文件夹并将文件移动到其中。
    4. 删除旧的文件层次结构。

    第 1 部分:获取文件路径列表。

    import os
    import glob
    import shutil
    
    all_files = [filename for filename in glob.iglob('**/*.txt', recursive=True)]
    

    第 2 部分:使用文件名创建子文件夹列表。

    # Function that takes a filename and returns desired subfolder and file paths as tuple. 
    def get_full_path(file):
        num = file[-6]
        let = file[-5]
        file_name = file.split('/')[-1]
        
        subfolders = f'folder{num}/folder{let}/'
        file_path = f'folder{num}/folder{let}/{file_name}'
        return subfolders, file_path
    
    # Call the function on each file to get all subfolders and file paths.
    subfolders = []
    full_paths = []
    for file in all_files:
        subfolder, full_path = get_full_path(file)
        subfolders.append(subfolder), full_paths.append(full_path)
    

    第 3 部分:创建所有子文件夹,将所有文件移入其中。

    for i in range(len(all_files)):
        os.makedirs(subfolders[i])
        os.rename(all_files[i], full_paths[i])
    

    第 4 部分:删除原始文件夹和子文件夹。

    old_folders = [x for x in os.listdir() if x[-1] in ['A', 'B', 'C']]
    for folder in old_folders:
        shutil.rmtree(folder, ignore_errors=False, onerror=None)
    

    如果您有文件夹“C”之外的文件夹,则需要在第 4 部分中展开 in ['A', 'B', 'C'] 列表。否则,这应该任意扩展。

    如果您有任何问题,请告诉我,祝您项目的其余部分好运!

    【讨论】:

    • @Allentro 这对你有用吗?需要帮助请叫我。谢谢,祝你好运!
    【解决方案2】:

    这是一个不假定特定文件夹名称的解决方案:

    from pathlib import Path
    
    def switch_parts(path):
        one, two, three, four = path.parts
        return Path(one, three, two, four)
    
    # generate new paths
    files = Path('master').glob('**/*.txt')
    rearranged = [switch_parts(f) for f in files]
    
    # create new folders and move files
    for old, new in zip(files, rearranged):
        new.parent.mkdir(parents=True, exist_ok=True)
        old.rename(new)
    
    # clean up old folders
    for old in files:
        old.parent.rmdir()
        try:
            old.parent.parent.rmdir()
        except OSError:
            pass # will be deleted eventually
    

    【讨论】:

    • 当旧的和重新排列的路径不是不同的集合时会出现一些问题(尝试使用.../folderX/folderX/...),但我喜欢这个想法(+1)
    • @VPfB 好点。这种风险似乎是增加灵活性的代价。
    【解决方案3】:

    你可以先获取文件的所有路径,然后使用递归创建新结构:

    import os, shutil, collections
    #generator function that finds all file paths in the target directory
    def get_files(d = os.getcwd(), p = None):
      for i in os.listdir(d):
         if os.path.isdir(_d:=os.path.join(d, i)):
            yield from get_files(d = _d, p = ([] if p is None else p)+[i])
         else:
            yield ([] if p is None else p)+[i]
    
    #group all reversed file paths from the function above on the leading dir name
    def new_structure(d, p = []):
       _dir = collections.defaultdict(list)
       for a, b, c in d:
          if not a:
             shutil.copyfile(c, os.path.join(os.getcwd(), *p, b))
          else:
             _dir[a[0]].append((a[1:], b, c))
       for a, b in _dir.items():
          os.mkdir(os.path.join(os.getcwd(), *(_p:=p+[a])))
          new_structure(b, p = _p)
    
    r = [[a[::-1], b, os.path.join(*a, b)] for *a, b in get_files()]
    new_structure(r)
    #remove old structure
    for (*_, a), _, _ in r:
       if os.path.isdir(a):
          shutil.rmtree(a, ignore_errors=False, onerror=None)
    

    此解决方案适用于任何深度的目录以及完全任意的目录和文件名。

    【讨论】:

      猜你喜欢
      • 2019-11-05
      • 2023-03-27
      • 1970-01-01
      • 2020-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-12
      • 2017-06-25
      相关资源
      最近更新 更多