我正在尝试创建一个表示目录树的递归数据结构。它将通过解析实际目录树来创建。结果应该看起来像这个简化的例子:
$structure = [
{
type => 'directory',
name => 'my_root_directory',
items => [
{
type => 'file',
name => 'image1.jpg'
},
{
type => 'file',
name => 'image2.jpg'
},
{
type => 'directory',
name => 'a_subdirectory',
items => [
{
type => 'image',
name => 'image2.jpg'
}
]
}
]
}
]
过去我通过编写递归函数创建了许多维护难题,所以这次我决定只使用File::Find
来处理我的目录。这是我试过的:
use strict;
use warnings;
use File::Find;
my $structure = [];
find(\&wanted, 'my_root_directory');
sub wanted {
my $node = {};
if (-f) {
$node->{type} = 'file';
$node->{name} = $_;
}
if (-d) {
$node->{type} = 'directory',
$node->{name} = $_;
$node->{items} = # HELP NEEDED!
}
push @{$structure}, $node;
}
这不起作用有两个原因:
$node->{items}
而无需复制File::Find
已经完成的工作。readdir
),最终的push
语句也只是将节点附加到结束@{$structure}
数组。所有等级都将丢失。如何使用File::Find
创建递归数据结构?
答案 0 :(得分:3)
您需要与源结构有多相似?我问的原因是 - 这样的事情可能有用:
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
use Data::Dumper;
use File::Spec::Functions qw(splitdir);
my %tree;
sub insert_into_tree {
my $cursor = \%tree;
print "$File::Find::dir = $File::Find::name\n";
foreach my $subdir ( splitdir($File::Find::dir) ) {
$cursor->{type} = 'directory';
$cursor = $cursor->{subdirs}->{$subdir} ||= {};
$cursor->{name} = $subdir;
}
if ( -f $File::Find::name ) {
push( @{ $cursor->{files} }, $_ );
}
else {
$cursor -> {type} = 'directory';
}
}
find( \&insert_into_tree, 'c:\\temp' );
print Dumper \%tree;
它没有做你想要的,但希望有助于说明一般概念?您根据当前文件路径创建一个“光标”,它是对树位置的引用。因此,您可以随意构建目录树,然后插入“文件”数组...所有文件。
你用File::Find
刷新的东西是,为你做了一个递归遍历(这很好,因为有了问题),但是wanted
函数本身并不是递归的 - 它是一个回调函数,并且每找到一个条目就会触发一次,并将3个变量用于处理。
如此有效 - 你仍然必须自己'建造'你的树。这是$cursor
上面做的事情 - 它正在根据传递给File::Find
的目录结构制作指向数据结构的指针。
答案 1 :(得分:3)
这将输出您要求的树结构中的文件内容。我使用散列%parent_nodes
来填充目录节点的引用,这些目录节点遇到目录节点,由目录路径索引。这使得引用适当的父目录节点以插入新文件节点成为可能。
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
use File::Basename qw(basename);
use Data::Dumper;
my $root_dir = shift @ARGV || die "Usage: $0 DIR_PATH\n";
my %parent_nodes;
my $structure = [];
find(\&wanted, $root_dir);
sub wanted {
my $file_name = $_;
my $file_path = $File::Find::name;
my $dir_path = $File::Find::dir;
if (-f) {
my $node = {
type => 'file',
name => $file_name,
};
my $parent_node = $parent_nodes{$dir_path}
|| die "can't find parent node for $dir_path\n";
push @{$parent_node->{items}}, $node;
}
elsif (-d) {
my $dir_name = basename($file_path);
my $node = {
type => 'directory',
name => $dir_name,
items => [],
};
if (my $parent_node = $parent_nodes{$dir_path}) {
push @{$parent_node->{items}}, $node;
}
else {
# this must be the root node
push @$structure, $node;
}
$parent_nodes{$file_path} = $node;
}
}
print Dumper($structure);