鉴于此目录结构:
└── names
├── name1
│ └── subdir1
│ ├── subnames
│ │ └── cool
│ │ ├── another-subdir1
│ │ │ ├── file
│ │ │ │ ├── file.txt
│ │ │ ├── subnames
│ │ │ │ ├── awesome
│ │ │ │ │ └── another-subdir2
│ │ │ │ │ ├── files
│ │ │ │ │ │ ├── file.txt
│ │ │ │ │ ├── subname
│ │ │ │ │ │ └── super-awesome
│ │ │ │ │ │ └── another-subdir3
│ │ │ │ │ │ ├── file
│ │ │ │ │ │ │ ├── file.txt
│ │ │ │ └── great
│ │ │ │ └── great-subdir1
│ │ │ │ ├── random-name
│ │ │ │ │ ├── file.txt
└── name2
└── subdir2
├── subnames
│ └── nice
│ ├── file.txt
我正在尝试遍历整个目录结构,但我很难让它工作。 我试过下面的代码,但它并没有完全给出我想要的:
import os
def get_full_names(parent_dir):
names = []
for dir_name in os.scandir(parent_dir):
if dir_name.is_dir():
names.append(dir_name.name)
subname_dir = os.path.join(parent_dir, dir_name.name)
for root, dirs, files in os.walk(subname_dir):
if 'subnames' in dirs:
subnames = get_full_names(os.path.join(root, 'subnames'))
for subname in subnames:
names.append(f"{subname}-{dir_name.name}")
return names
parent_dir = '/path/to/names'
names = get_full_names(parent_dir)
print(names)
此代码给出:
['name1', 'cool-name1', 'great-cool-name1', 'awesome-cool-name1', 'super-awesome-awesome-cool-name1', 'super-awesome-cool-name1', 'great-name1', 'awesome-name1', 'super-awesome-awesome-name1', 'super-awesome-name1', 'name2', 'nice-name2']
预期产出:
['name1', 'cool-name1', 'awesome-cool-name1', 'super-awesome-awesome-cool-name1', 'great-cool-name1', 'name2', 'nice-name2']
在这种情况下,它应该排除
'super-awesome-cool-name1', 'great-name1', 'awesome-name1', 'super-awesome-awesome-name1' 'super-awesome-name1'
,因为:
super-awesome不直接是subname/cool子目录
great 不直接是 names/name1 sudirectory
awesome 不直接是 names/name1 子目录
等等……
图案永远是
names/parent-names-that-will-change/any-name-that-might-change/subnames/sub-parent-name/any-name-that-might-change/subnames/another-sub-parent-name/any-name-that-might-change/last-sub-dir-name
上面的例子应该变成
last-sub-dir-name/another-sub-parent-name/sub-parent-name/parent-names-that-will-change
请注意,我使用斜杠“/”只是为了区分“-”。
基本上,我只想根据它的直接父子目录到根父目录(name1 和 name2)加入子名称。
试试这个:
import os
import re
def getFullNames(parentDir):
results = []
for (path, _, _) in os.walk(parentDir):
names = []
names.extend(re.findall(r"(?:^names|subnames?)\\([^\\]+)", path))
names.reverse()
fullName = "-".join(names)
if fullName not in results:
results.append(fullName)
return results[1:]
构建您描述的目录结构并运行
print(getFullNames("MY_PATH_TO_THE_PARENT_FOLDER"))
后,我得到了您预期的输出:
['name1', 'cool-name1', 'awesome-cool-name1', 'super-awesome-awesome-cool-name1', 'great-cool-name1', 'name2', 'nice-name2']
不是尝试以巧妙的方式遍历目录结构,而是遍历
os.walk()
找到的每条路径,使用正则表达式计算路径应生成的名称,然后将其添加到结果列表(如果尚未生成)找到了。
为了生成名称,它使用名称中包含的每个子目录必须是名为“subname”或“subnames”的子目录的直接子目录的规则。
您可以在带有
'subname'
的名称目录下立即加入名称,并使用递归遍历函数生成的路径。使递归函数将一个标志作为附加参数来指示当前给定目录是否应该直接在其下产生目录名称,这样您就不必再次解析它:
import os
def get_full_names(parent_dir):
def _get_paths(parent_dir, contains_names=True):
for entry in os.scandir(parent_dir):
if contains_names:
yield [entry.name]
if entry.is_dir():
for path in _get_paths(entry.path, 'subname' in entry.name):
yield path + ([entry.name] if contains_names else [])
return ['-'.join(path) for path in _get_paths(parent_dir)]
鉴于您问题中的目录结构,上述函数将返回:
['name1', 'cool-name1', 'awesome-cool-name1', 'super-awesome-awesome-cool-name1', 'great-cool-name1', 'name2', 'nice-name2']
您可以跟踪每个子目录的父目录,并且仅如果它是名称/名称1或名称/名称2的直接子目录,则附加子目录名称。
例子:
import os
def get_full_names(parent_dir, parent_name=""):
names = []
for dir_name in os.scandir(parent_dir):
if dir_name.is_dir():
if parent_name:
subname_dir = os.path.join(parent_dir, parent_name, dir_name.name)
else:
subname_dir = os.path.join(parent_dir, dir_name.name)
if os.path.exists(os.path.join(subname_dir, "subnames")):
subnames = get_full_names(subname_dir, "subnames")
for subname in subnames:
if parent_name:
full_name = f"{subname}-{parent_name}"
else:
full_name = subname
names.append(full_name)
else:
if parent_name:
full_name = f"{dir_name.name}-{parent_name}"
else:
full_name = dir_name.name
names.append(full_name)
return names
parent_dir = '/path/to/names'
names = get_full_names(parent_dir)
print(names)
过于简单的解释:
想象你的目录是盒子。假设我们想知道每个盒子的名称,但我们也想知道它在哪个更大的盒子里。我们可以从最大的盒子开始,也就是所谓的“名字”,看看里面有什么。
然后,我们可以查看“名称”框内的每个框,看看它们里面有什么。如果我们在那个盒子里面找到一个叫做“subnames”的盒子,我们可以记住它的名字和它所在的盒子的名字。
我们一直这样做,直到我们查看了每个盒子的内部。然后,我们把我们记住的所有名字放在一个列表中,这样我们就知道每个盒子的名字以及它在哪个更大的盒子里。
我知道我迟到了,但是这里发布的答案都不够优雅。
我的代码:
import re
from pathlib import Path
def get_full_names(cur_dir: Path, names: list = None, root: list = None):
if names is None:
names = []
if root is None:
root = []
if re.match('^(sub)?names?$', cur_dir.parent.name):
root = [*root, cur_dir.name]
names.append('-'.join(root[::-1]))
for p in cur_dir.iterdir():
if p.is_dir():
get_full_names(p, names, root)
return names
输出:
In [22]: get_full_names(Path('D:/test/names'))
Out[22]:
['name1',
'cool-name1',
'awesome-cool-name1',
'super-awesome-awesome-cool-name1',
'great-cool-name1',
'name2',
'nice-name2']
这个问题是一个很好的案例,使用一个简单的递归函数,只需将实际工作放在函数本身,递归调用函数本身即可。
在这种情况下,要实现你想要的,你只需要检查当前路径是否是一个目录,如果当前路径是一个目录,则检查当前目录的父路径是否在
{'name', 'names', 'subname', 'subnames'}
中,如果父即,将当前名称附加到root
列表,使用'-'以相反的顺序加入root
,并收集结果。
由于父名称必须包含
'name'
,并且可以在开头有 sub
和/或在末尾有 s
,这个正则表达式 '^(sub)?names?$'
可以用来验证这样的。
通过遍历当前目录的子目录并递归调用该函数,确保遍历目录的所有级别,并且第一个参数是目录(当然除非您使用非目录参数调用该函数).
而且我没有使用
os
,因为 Python 3 默认带有 pathlib
,它是专门为这类事情设计的标准库,并且在这方面远远优于 os
。
我使用以下内容创建了测试示例:
from pathlib import Path
files = [
'D:/test/names/name1/subdir1/subnames/cool/another-subdir1/file/file.txt',
'D:/test/names/name1/subdir1/subnames/cool/another-subdir1/subnames/awesome/another-subdir2/files/file.txt',
'D:/test/names/name1/subdir1/subnames/cool/another-subdir1/subnames/awesome/another-subdir2/subname/super-awesome/another-subdir3/file/file.txt',
'D:/test/names/name1/subdir1/subnames/cool/another-subdir1/subnames/great/great-subdir1/random-name/file.txt',
'D:/test/names/name2/subdir2/subnames/nice/file.txt'
]
for file in files:
Path(file).parent.mkdir(parents=True, exist_ok=True)
Path(file).touch()
目录结构:
PS D:\test> tree /f D:\test\names
Folder PATH listing for volume Tremillia
Volume serial number is 000000BD 4326:4555
D:\TEST\NAMES
├───name1
│ └───subdir1
│ └───subnames
│ └───cool
│ └───another-subdir1
│ ├───file
│ │ file.txt
│ │
│ └───subnames
│ ├───awesome
│ │ └───another-subdir2
│ │ ├───files
│ │ │ file.txt
│ │ │
│ │ └───subname
│ │ └───super-awesome
│ │ └───another-subdir3
│ │ └───file
│ │ file.txt
│ │
│ └───great
│ └───great-subdir1
│ └───random-name
│ file.txt
│
└───name2
└───subdir2
└───subnames
└───nice
file.txt