PHP中将路径字符串拆分为广度优先数组的简单方法


simple way in PHP to split path string into breadth-first array

使用PHP 5.2,我试图将任意数量的路径/目录字符串解析为一个数组,这样就可以先处理广度。这是为了让我能够从Subversion存储库中编写稀疏签出的脚本,伸缩指示的路径。必须在同一svn update --depth empty语句中指定同一级别上的所有路径。

我得到了想要的输出,但我想知道是否有更干净的方法。(是的,我知道效率需要改变。)

EDIT我修改了原来的帖子,以处理同一父级中多个子级的情况。我修改后的代码是

$main = array(
    'a/b/c1/',
    'a/b/c2/d/e1',
    'a/b/c2/d/e2',
    'A/B/',
    'alpha/beta/gamma/delta/epsilon'
);
$splits = array();
$max = 0;
for ($i=0; $i<count($main); $i++) {
    $splits[$i] = explode(DIRECTORY_SEPARATOR, trim($main[$i], DIRECTORY_SEPARATOR));
    if (count($splits[$i]) > $max) {
        $max = count($splits[$i]);
    }
}
for ($i=0; $i<$max; $i++) {
    $levels[$i] = array();
    for ($path=0; $path<count($splits); $path++) {
        if (array_key_exists($i, $splits[$path])) {
            $levels[$i][] = implode(DIRECTORY_SEPARATOR, array_slice($splits[$path], 0, $i+1));
        }
    }
    $levels[$i] = array_unique($levels[$i]);
    sort($levels[$i]);  // just to reset indices
}

这将我的输出结构更改为以下结构,既在每个级别提供了唯一的目录,又保留了同级节点。

Array
(
    [0] => Array
        (
            [0] => A
            [1] => a
            [2] => alpha
        )
    [1] => Array
        (
            [0] => A/B
            [1] => a/b
            [2] => alpha/beta
        )
    [2] => Array
        (
            [0] => a/b/c1
            [1] => a/b/c2
            [2] => alpha/beta/gamma
        )
    [3] => Array
        (
            [0] => a/b/c2/d
            [1] => alpha/beta/gamma/delta
        )
    [4] => Array
        (
            [0] => a/b/c2/d/e1
            [1] => a/b/c2/d/e2
            [2] => alpha/beta/gamma/delta/epsilon
        )
)

在我的代码中,我对最终的$levels数组进行迭代。不幸的是,这仍然需要两次迭代:一次用于depth empty,一次用于depth infinity,但我相信这是可以实现的。

$count = count($levels);
for ($i=0; $i<$count; $i++) {
    echo '<p>', 'svn update --set-depth empty ', implode(' ', $levels[$i]), "</p>'n";
}
$count = count($main);
for ($i=0; $i<$count; $i++) {
    echo '<p>', 'svn update --set-depth infinity ', $main[$i], "</p>'n";
}
$levels=array();
$depth=0;
$i=0;
foreach ($main as $m) {
  $m=explode('/',$m);
  while (sizeof($m)<$depth) $m[]=null;
  $d=0;
  foreach ($m as $mm) {
    if ($d>$depth) {
      if (!$mm) break;
      $depth=$d;
      $levels[$d]=array();
      for ($j=0;$j<=$i;$j++) $levels[$d][$j]=null;
    }
    $levels[$d][$i]=$mm;
    $d++;
  }
  $i++;
}

看起来是一个很好的选择,只需要遍历一次数组。简言之,您不使用一次传递来决定深度,但如果遇到更深的条目,您只需用null追溯填充数组中的相关位置。

$depth在循环之后具有depth-1。

编辑:

这还可以处理同一个父级中有多个子级的情况,但我不确定它是否按照您想要的方式来处理

以下是我的实现(随着您的最新请求,它变得更容易了):

$levels = array();
for ($i=0; $i<count($main); $i++) {
        $splits = explode(DIRECTORY_SEPARATOR, trim($main[$i], DIRECTORY_SEPARATOR));
        $current = array();
        /* Load every subpath in an array*/
        for($j=0; $j<count($splits); $j++) {
                $current[$j . "hack"] = implode("/", array_slice($splits, 0, $j+1));
        }
        $levels = array_merge_recursive($levels, $current);
}
/* Removes duplicates and resets indices */
array_walk($levels, function(&$l, $i) { $l = array_unique($l); sort($l); });

这个实现之所以简单,是因为我分别处理每个路径。我只有一个循环,并使用array_merge_recursive连接结果。例如,对于"a/b"answers"a/c",我的代码是:

  1. 创建array(0 => array("a"), 1 => array("a/b"))array(0 => array("a"), 1 => array("a/c"))
  2. 将它们与array_merge_recursive结合,从而得到array(0 => array("a", "a"), 1 => array("a/b", "a/c"))
  3. 使用array_unique删除唯一值
  4. sort重置索引

注意:我需要使用$j + "hack",否则array_merge_recursive不会按预期合并值(自己试试)。