使用 PHP 如何使用递归编程和 mkdir() 来创建此层次结构?

发布于 2024-12-05 12:08:34 字数 1562 浏览 1 评论 0原文

我想创建一个基于分区映射的目录结构,如这些示例所示。我尝试过各种方法都失败了。递归编程让我头疼!

示例 1

$partitionMap = '#/#'; //An example of two levels deep

myfolder/0
myfolder/1
myfolder/2  
...
myfolder/f
myfolder/f/0
myfolder/f/1
myfolder/f/2
myfolder/f/3
...

示例 2

$partitionMap = '#/#/#'; //An example of three levels deep

myfolder/a
myfolder/a/4
myfolder/a/4/f

示例 3

$partitionMap = '#/##/###'; //An example of three levels deep

myfolder/0
myfolder/1
myfolder/2
...
myfolder/a/00
myfolder/a/01
myfolder/e/02
myfolder/e/03
...
myfolder/e/03/af0
myfolder/e/03/af1
myfolder/e/03/af2
myfolder/e/03/af3

# 是一个十六进制占位符,表示 0-F,如上面所示。

所以我需要像这样工作的东西..

$partitionMap = '#/##'; 

makeFoldersRecursive( './myfolder', $partitionMap );

function makeFoldersRecursive( $path, $pmap ){
    $pmapArr = explode( '/', $pmap );

    $numNibbles = strlen( $pmapArr[0] );
    //Get hex folder count but in dec, # = 16, ## = 256, ### = 4096
    $folder_count_this_level = pow( 16, $numNibbles );

    for($i=0; $i<$count; $i++ ) {
        //Get left padded hex folder name from $i
        $folder = sprintf("%0" . $numNibbles . "s", dechex($i));
        mkdir( $path .  DIRECTORY_SEPARATOR . $folder , 0755 );

        if( array_key_exists( 1, $pmapArr ) ) {
            $passMap = $pmapArr; 
            array_shift( $pmapArr );
            makeFoldersRecursive( $path .  DIRECTORY_SEPARATOR . $folder, $passMap );
        }
    }



}

必须有一种更优雅的方法来做到这一点,任何帮助将不胜感激!

I want to create a directory structure based on a partition map, as in these examples. I've tried various approaches with failure. Recursive programming makes my brain hurt!

Example 1

$partitionMap = '#/#'; //An example of two levels deep

myfolder/0
myfolder/1
myfolder/2  
...
myfolder/f
myfolder/f/0
myfolder/f/1
myfolder/f/2
myfolder/f/3
...

Example 2

$partitionMap = '#/#/#'; //An example of three levels deep

myfolder/a
myfolder/a/4
myfolder/a/4/f

Example 3

$partitionMap = '#/##/###'; //An example of three levels deep

myfolder/0
myfolder/1
myfolder/2
...
myfolder/a/00
myfolder/a/01
myfolder/e/02
myfolder/e/03
...
myfolder/e/03/af0
myfolder/e/03/af1
myfolder/e/03/af2
myfolder/e/03/af3

The # is a hexadecimal placeholder presenting 0-F as you can see above.

So I need something that works like this..

$partitionMap = '#/##'; 

makeFoldersRecursive( './myfolder', $partitionMap );

function makeFoldersRecursive( $path, $pmap ){
    $pmapArr = explode( '/', $pmap );

    $numNibbles = strlen( $pmapArr[0] );
    //Get hex folder count but in dec, # = 16, ## = 256, ### = 4096
    $folder_count_this_level = pow( 16, $numNibbles );

    for($i=0; $i<$count; $i++ ) {
        //Get left padded hex folder name from $i
        $folder = sprintf("%0" . $numNibbles . "s", dechex($i));
        mkdir( $path .  DIRECTORY_SEPARATOR . $folder , 0755 );

        if( array_key_exists( 1, $pmapArr ) ) {
            $passMap = $pmapArr; 
            array_shift( $pmapArr );
            makeFoldersRecursive( $path .  DIRECTORY_SEPARATOR . $folder, $passMap );
        }
    }



}

There's got to be a more elegant way of doing this, Any help would be appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

罗罗贝儿 2024-12-12 12:08:34

更纯粹的递归解决方案

您已经有了一个,但我将对其进行另一种旋转,在我看来,它看起来“更纯粹”,并解释我是如何到达那里的。

代码如下:

function create_all_dirs($directory, $map) {
    // 1: TERMINATION CONDITION
    if (empty($map)) {
        mkdir($directory, 0755, true);
        return;
    }

    // 2: PROCESS PART OF DATA, PREPARE DATA TO PASS ON RECURSION
    $parts = explode('/', $map);
    $partLength = strlen(array_shift($parts));
    $dirCount = pow(16, $partLength);    
    $map = implode('/', $parts);

    // 3: RECURSE
    for($i = 0; $i < $dirCount; ++$i) {
        $dir = $directory.DIRECTORY_SEPARATOR
                         .sprintf('%0'.$partLength.'s', dechex($i));
        create_all_dirs($dir, $map);
    }
}

解释

虽然我什至不会建议有一种好的方法可以实现递归函数的解决方案,但我可以解释一种在实践中对我来说效果很好的方法。

第 1 步:考虑递归时需要传递哪些数据。

在这种情况下,函数的每一级都将负责创建一级目录结构。因此,如果您

create_all_dirs('c:/', '#/#')

在某个时刻调用 then ,该函数将进行如下所示的递归调用,这是有道理的:

create_all_dirs('c:/0', '#')

这就是您此时需要确定的全部内容。您可以看出这是有道理的,并且在一系列此类调用之后,该函数将(不知何故,我们还没有编写它)完成其工作。

第 2 步:终止条件

有了这些知识,就可以确定函数的终止条件。在我们的例子中,考虑到上述情况,合理的终止条件是“当第二个参数是空字符串时”。那么如果使用目录和空“map”字符串调用该函数会做什么?

function create_all_dirs($directory, $map) {
    if(empty($map)) {
        mkdir($directory, 0755, true);
        return;
    }

    // ???
}

显然它应该创建目录。我以这样的方式调用 mkdir ,它将递归地创建路径上的所有目录(如果它们尚不存在),因为最终的解决方案需要它才能工作,但这不是必需的。如果没有这种便利,您已经知道如何做到这一点。

第三步:准备所有递归调用通用的参数

好的,那么我们需要什么来进行递归调用呢?您已经知道,在每个级别,我们都需要弹出 $map 字符串的一部分并递归调用该函数多次。我们还需要为其提供一个新的、更深的目录字符串和一个新的、更短的映射字符串(所有这些您都可以从步骤 1 的结论中看出,即使您尚未创建解决方案)。

所以我们有:

// cut off part of $map; we also need the length of the part we cut
// off, to know how many times we need to recursively call at this level
$parts = explode('/', $map);
$partLength = strlen(array_shift($parts));

// how many recursive calls?
$dirCount = pow(16, $partLength);

// this will be the $map parameter for all recursive calls
$map = implode('/', $parts);

第 4 步:递归调用函数

此时我认为没有什么需要解释的了。我们计算 $directory 的值,该值对于每次递归调用都是不同的,并将其设为:

for($i = 0; $i < $dirCount; ++$i) {
    $dir = $directory.DIRECTORY_SEPARATOR
                     .sprintf('%0'.$partLength.'s', dechex($i));
    create_all_dirs($dir, $map);
}

A purer recursive solution

You already have one, but I 'll give another spin on it that looks "purer" to my eyes and also explain how I got there.

Here's the code:

function create_all_dirs($directory, $map) {
    // 1: TERMINATION CONDITION
    if (empty($map)) {
        mkdir($directory, 0755, true);
        return;
    }

    // 2: PROCESS PART OF DATA, PREPARE DATA TO PASS ON RECURSION
    $parts = explode('/', $map);
    $partLength = strlen(array_shift($parts));
    $dirCount = pow(16, $partLength);    
    $map = implode('/', $parts);

    // 3: RECURSE
    for($i = 0; $i < $dirCount; ++$i) {
        $dir = $directory.DIRECTORY_SEPARATOR
                         .sprintf('%0'.$partLength.'s', dechex($i));
        create_all_dirs($dir, $map);
    }
}

Explanation

While I won't even suggest that there's one good way of reaching a solution for recursive functions, I can explain an approach that has worked well in practice for me.

Step 1: Think about what data you will need to pass when recursing.

In this case, every level of the function will be responsible for creating one level of the directory structure. So it makes sense that if you call

create_all_dirs('c:/', '#/#')

then at some point the function will make a recursive call that looks like this:

create_all_dirs('c:/0', '#')

That's all you need to pin down at this point. You can tell that it makes sense and that after a sequence of such calls the function will have (somehow, we haven't yet written it) done its job.

Step 2: Termination condition

With this knowledge, pin down your function's termination condition. In our case, given the above a reasonable termination condition would be "when the second parameter is the empty string". So what would the function do if called with a directory and an empty "map" string?

function create_all_dirs($directory, $map) {
    if(empty($map)) {
        mkdir($directory, 0755, true);
        return;
    }

    // ???
}

Obviously it should create the directory. I 'm calling mkdir in such a way that it will recursively create all directories on the path if they don't already exist because the final solution needs it to work, but that's not necessary. You already know how to do it without this convenience.

Step 3: Prepare the parameters common to all recursive calls

OK, so what do we need to make the recursive call? You already know that at each level we need to pop part of $map string and call the function recursively a number of times. We also need to feed it a new, deeper directory string and a new, shorter map string (all of this you could tell from the conclusion of step 1, even if you had not already created a solution).

So we have:

// cut off part of $map; we also need the length of the part we cut
// off, to know how many times we need to recursively call at this level
$parts = explode('/', $map);
$partLength = strlen(array_shift($parts));

// how many recursive calls?
$dirCount = pow(16, $partLength);

// this will be the $map parameter for all recursive calls
$map = implode('/', $parts);

Step 4: Call the function recursively

At this point I don't think there is anything more to explain. We calculate the value of $directory, which is different for each recursive call, and make it:

for($i = 0; $i < $dirCount; ++$i) {
    $dir = $directory.DIRECTORY_SEPARATOR
                     .sprintf('%0'.$partLength.'s', dechex($i));
    create_all_dirs($dir, $map);
}
孤檠 2024-12-12 12:08:34

递归并不是真正需要的,只会让事情变得更加混乱。这个方案直接使用队列比较直接。

因此,假设您有一个像“#/#”这样的模式,您可以将其推入队列中。然后将其取下,并将其中的第一个“#”替换为您的一个字符。这有效地将队列中的项目数乘以 16。您重复此过程,取出一个项目,进行替换,然后将其放回原处,直到替换了队列中每个项目中的每个“#”。

然后您可以像往常一样创建目录。

$partitionMap = '#/##';

makeFolders('./myfolder', $partitionMap);

function makeFolders($path, $pmap) {

  // Use number of characters and number of '#' to compute total combinations
  $characters = array(
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
  );
  $count = substr_count($pmap, '#');
  $num_results = pow(count($characters), $count);

  // Start array with path and pmap
  $combos = array( $path . '/' . $pmap );

  // Go through array, replacing each '#' with one of the characters.
  while (count($combos) < $num_results) {
    $str = array_shift($combos);

    foreach ($characters as $c) {
      array_push($combos, preg_replace('/#/', $c, $str, 1));
    }
  }

  // Create the directories, using third 'TRUE' parameter
  // to avoid creating parents manually
  foreach ($combos as $dir) {
    mkdir($dir, 0755, TRUE);
  }
}

Recursion isn't really needed and just makes things more confusing. This solution uses a queue directly to be more straightforward.

So, assuming you have a pattern like '#/#', you push that into a queue. Then you take it off, and replace the first '#' in it with one of your characters. This effectively multiplies the number of items in the queue by 16. You repeat this process, taking an item off, doing the replacement, and putting them back in, until you have replaced every '#' in every item in the queue.

Then you can just create the directories as usual.

$partitionMap = '#/##';

makeFolders('./myfolder', $partitionMap);

function makeFolders($path, $pmap) {

  // Use number of characters and number of '#' to compute total combinations
  $characters = array(
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
  );
  $count = substr_count($pmap, '#');
  $num_results = pow(count($characters), $count);

  // Start array with path and pmap
  $combos = array( $path . '/' . $pmap );

  // Go through array, replacing each '#' with one of the characters.
  while (count($combos) < $num_results) {
    $str = array_shift($combos);

    foreach ($characters as $c) {
      array_push($combos, preg_replace('/#/', $c, $str, 1));
    }
  }

  // Create the directories, using third 'TRUE' parameter
  // to avoid creating parents manually
  foreach ($combos as $dir) {
    mkdir($dir, 0755, TRUE);
  }
}
朕就是辣么酷 2024-12-12 12:08:34

我在互联网上的某个地方找到了这个。如果您不使用相对路径,似乎可以工作。即“../basepath/newpath1/newpath2”。仍在尝试弄清楚。

/**
 * recursively create a long directory path
 */
function createPath($path, $rwd=0777) {
    if (is_dir($path)) return true;
    rev_path = substr($path, 0, strrpos($path, '/', -2) + 1 );
$return = createPath($prev_path);
    return ($return && is_writable($prev_path)) ? mkdir($path, $rwd, true) : false;
}

希望它对某人有帮助。我认为它相当优雅。
只是评论一下,递归是所有编程语言中非常常见的做法,并且有它的地位。尝试去理解它,不要仅仅因为你拒绝学习如何编码就诋毁它。例如,尝试在没有递归或函数的情况下获取文件列表,您很快就会迷失方向。

I found this on the internet somewhere. seems to work if you're not using relative path. i.e. "../basepath/newpath1/newpath2". Still trying to figure it out.

/**
 * recursively create a long directory path
 */
function createPath($path, $rwd=0777) {
    if (is_dir($path)) return true;
    rev_path = substr($path, 0, strrpos($path, '/', -2) + 1 );
$return = createPath($prev_path);
    return ($return && is_writable($prev_path)) ? mkdir($path, $rwd, true) : false;
}

Hope it helps somebody. I think its fairly elegant.
Just a comment, recursion is a very common practice in all programming languages and has it's places. Try to understand it, don't denigrate it just because you refuse to learn how to code it. As an example, try to get a file list without recursion or functions and you'll lose yourself very quickly.

爱*していゐ 2024-12-12 12:08:34

只要您不需要长目录名称(在 32 位系统上最多 8 个十六进制数字),您就可以使用它(所有三个版本都具有恒定的内存复杂性):

// Example for "#/##"
for ($i = 0; $i <= 0x0FFF; $i++) {
    $d = sprintf("%01x/%02x", $i & 0x00F, ($i & 0xFF0) >> 4);
    echo $d, "\n";
    mkdir($d, 0777, true);
}

并使用您的格式字符串:

function mkhexdir($format) {
    // Parse format string
    $fmt_N = array_map('strlen', explode('/', $format));
    $bit_count = array_sum($fmt_N) * 4;
    $max = 1 << $bit_count;

    // Prepare format string for sprintf
    $fmt_printf = join('/', array_map(function($n) { return '%0'.$n.'x';}, $fmt_N));

    // Generate all dirs
    for ($i = 0; $i < $max; $i++) {
        $args = array();
        $offset = $bit_count;
        foreach ($fmt_N as $len) {
            $offset -= $len * 4;
            $b = (1 << ($len * 4)) - 1;
            $args[] = ($i >> $offset) & $b;
        }
        $d = vsprintf($fmt_printf, $args);
        echo $d, "\n";
        mkdir($d, 0777, true);
    }
}

mkhexdir('#/##/##');

或使用常规的另一个版本表达:

function mkhexdir($format) {
    // Parse format string
    $fmt_N = array_map('strlen', explode('/', $format));
    $digits = array_sum($fmt_N);
    $max = 1 << ($digits * 4);

    // Prepare format string for sprintf
    $fmt_printf = '%0'.$digits.'x';

    // Prepare regular expression to format path
    $src = '/^('.join(')(',
            array_map(function($n){ return str_repeat('.', $n);}, $fmt_N))
            .')$/';
    $dst = '\\'.join('/\\',
            array_map(function($n){ return $n + 1;}, array_keys($fmt_N)));

    // Generate all dirs
    for ($i = 0; $i < $max; $i++) {
        $d = preg_replace($src, $dst, sprintf($fmt_printf, $i));
        echo $d, "\n";
    }
}

mkhexdir('#/##/##');

As long as you do not need long directory names (up to 8 hex digits on 32 bit systems), you can use this (all three versions have constant memory complexity):

// Example for "#/##"
for ($i = 0; $i <= 0x0FFF; $i++) {
    $d = sprintf("%01x/%02x", $i & 0x00F, ($i & 0xFF0) >> 4);
    echo $d, "\n";
    mkdir($d, 0777, true);
}

And with use of your format strings:

function mkhexdir($format) {
    // Parse format string
    $fmt_N = array_map('strlen', explode('/', $format));
    $bit_count = array_sum($fmt_N) * 4;
    $max = 1 << $bit_count;

    // Prepare format string for sprintf
    $fmt_printf = join('/', array_map(function($n) { return '%0'.$n.'x';}, $fmt_N));

    // Generate all dirs
    for ($i = 0; $i < $max; $i++) {
        $args = array();
        $offset = $bit_count;
        foreach ($fmt_N as $len) {
            $offset -= $len * 4;
            $b = (1 << ($len * 4)) - 1;
            $args[] = ($i >> $offset) & $b;
        }
        $d = vsprintf($fmt_printf, $args);
        echo $d, "\n";
        mkdir($d, 0777, true);
    }
}

mkhexdir('#/##/##');

Or another version that uses regular expression:

function mkhexdir($format) {
    // Parse format string
    $fmt_N = array_map('strlen', explode('/', $format));
    $digits = array_sum($fmt_N);
    $max = 1 << ($digits * 4);

    // Prepare format string for sprintf
    $fmt_printf = '%0'.$digits.'x';

    // Prepare regular expression to format path
    $src = '/^('.join(')(',
            array_map(function($n){ return str_repeat('.', $n);}, $fmt_N))
            .')$/';
    $dst = '\\'.join('/\\',
            array_map(function($n){ return $n + 1;}, array_keys($fmt_N)));

    // Generate all dirs
    for ($i = 0; $i < $max; $i++) {
        $d = preg_replace($src, $dst, sprintf($fmt_printf, $i));
        echo $d, "\n";
    }
}

mkhexdir('#/##/##');
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文