【问题标题】:Remove empty subfolders with PHP使用 PHP 删除空子文件夹
【发布时间】:2010-12-22 10:55:50
【问题描述】:

我正在开发一个 PHP 函数,该函数将递归删除所有从给定绝对路径开始不包含文件的子文件夹。

这是目前开发的代码:

function RemoveEmptySubFolders($starting_from_path) {

    // Returns true if the folder contains no files
    function IsEmptyFolder($folder) {
        return (count(array_diff(glob($folder.DIRECTORY_SEPARATOR."*"), Array(".", ".."))) == 0);
    }

    // Cycles thorugh the subfolders of $from_path and
    // returns true if at least one empty folder has been removed
    function DoRemoveEmptyFolders($from_path) {
        if(IsEmptyFolder($from_path)) {
            rmdir($from_path);
            return true;
        }
        else {
            $Dirs = glob($from_path.DIRECTORY_SEPARATOR."*", GLOB_ONLYDIR);
            $ret = false;
            foreach($Dirs as $path) {
                $res = DoRemoveEmptyFolders($path);
                $ret = $ret ? $ret : $res;
            }
            return $ret;
        }
    }

    while (DoRemoveEmptyFolders($starting_from_path)) {}
}

根据我的测试,这个函数可以工作,尽管我很高兴看到任何可以提高代码性能的想法。

【问题讨论】:

  • @Ben - 来自 TS - '我很高兴看到任何关于性能更好的代码的想法。'
  • 最好在refactormycode.com 发帖?

标签: php directory


【解决方案1】:

如果空文件夹中的空文件夹中有空文件夹,则需要循环遍历所有文件夹 3 次。所有这一切,因为你首先要广度 - 在测试文件夹之前测试它的孩子。相反,您应该在测试 parent 是否为空之前进入子文件夹,这样一次就足够了。

function RemoveEmptySubFolders($path)
{
  $empty=true;
  foreach (glob($path.DIRECTORY_SEPARATOR."*") as $file)
  {
     if (is_dir($file))
     {
        if (!RemoveEmptySubFolders($file)) $empty=false;
     }
     else
     {
        $empty=false;
     }
  }
  if ($empty) rmdir($path);
  return $empty;
}

顺便说一句,glob 不会返回。和..条目。

短版:

function RemoveEmptySubFolders($path)
{
  $empty=true;
  foreach (glob($path.DIRECTORY_SEPARATOR."*") as $file)
  {
     $empty &= is_dir($file) && RemoveEmptySubFolders($file);
  }
  return $empty && rmdir($path);
}

为了检查隐藏文件,我更新了返回语句行

    function RemoveEmptySubFolders($path)
    {
        $empty = true;
        foreach (glob($path . DIRECTORY_SEPARATOR . "*") as $file) {
            $empty &= is_dir($file) && RemoveEmptySubFolders($file);
        }
        return $empty && (is_readable($path) && count(scandir($path)) == 2) && rmdir($path);
    }

【讨论】:

  • 优秀的算法!为了使该功能正常工作,应更正两个小错误: 1. 在 foreach 语句中应将 $folder 更改为 $path。 2. $file 应该作为参数传递给递归函数调用,而不是 $starting_from_path。
  • 请注意,如果您的函数在 Linux 下运行,隐藏的子目录(名称以点开头)也会被错误地匹配为“空”目录。
  • 如何修改它以不删除正在传递的根文件夹?如果原始 $path 只包含空文件夹,那么它也会被删除。没什么大不了的,我只是好奇:)
  • @swl1020,最简单的方法是将参数更改为 RemoveEmptySubFolders($path, $root = true) 然后当您在递归循环中调用此函数时,您将 RemoveEmptySubFolder($file, false ),然后返回语句应该是 $empty && (!$root) && rmdir($path);你可以像以前一样调用这个函数。
  • glob($path . DIRECTORY_SEPARATOR . '{,.}[!.,!..]*',GLOB_MARK | GLOB_BRACE) 如果您想避免在有某些文件时出现错误会更好以点开头的示例。
【解决方案2】:

Linux 解决方案,使用命令行工具,但比纯 PHP 更快、更简单

/**
 * Remove all empty subdirectories
 * @param string $dirPath path to base directory
 * @param bool $deleteBaseDir - Delete also basedir if it is empty
 */
public static function removeEmptyDirs($dirPath, $deleteBaseDir = false) {

    if (stristr($dirPath, "'")) {
        trigger_error('Disallowed character `Single quote` (\') in provided `$dirPath` parameter', E_USER_ERROR);
    }

    if (substr($dirPath, -1) != '/') {
        $dirPath .= '/';
    }

    $modif = $deleteBaseDir ? '' : '*';
    exec("find '".$dirPath."'".$modif." -empty -type d -delete", $out);
}

如果您需要 Windows 支持,请使用 PHP_OS 常量和此单行代码

for /f "delims=" %d in ('dir /s /b /ad ^| sort /r') do rd "%d"`enter code here

【讨论】:

    【解决方案3】:

    您可以执行 unix 命令来删除空目录。

    exec("find $starting_from_path -type d -empty -exec rmdir {} \; 2>/dev/null");

    【讨论】:

    • 为什么不直接find $dir -empty -type d -delete
    • 不,你不能。因为你永远不应该使用“exec”函数。这是危险的,应该在任何网络服务器环境中禁用。唯一可接受的用途是在使用 php-cli 时。
    【解决方案4】:

    你可以试试这个。

    function removeEmptySubfolders($path){
    
      if(substr($path,-1)!= DIRECTORY_SEPARATOR){
        $path .= DIRECTORY_SEPARATOR;
      }
      $d2 = array('.','..');
      $dirs = array_diff(glob($path.'*', GLOB_ONLYDIR),$d2);
      foreach($dirs as $d){
        removeEmptySubfolders($d);
      }
    
      if(count(array_diff(glob($path.'*'),$d2))===0){
        $checkEmpSubDir = explode(DIRECTORY_SEPARATOR,$path);
        for($i=count($checkEmpSubDir)-1;$i>0;$i--){
          $path = substr(str_replace($checkEmpSubDir[$i],"",$path),0,-1);
    
          if(($files = @scandir($path)) && count($files) <= 2){
            rmdir($path);
          }
        }
      }
    }
    

    【讨论】:

      【解决方案5】:

      这会带来麻烦,因为多次调用RemoveEmptySubFolders 可能会拼写错误,因为每次调用该函数时,都会再次定义其他两个函数。如果它们已经被定义,PHP 将抛出一个错误,指出已经定义了同名函数。

      改为递归尝试:

      function removeEmptySubfolders($path){
      
        if(substr($path,-1)!= DIRECTORY_SEPARATOR){
          $path .= DIRECTORY_SEPARATOR;
        }
        $d2 = array('.','..');
        $dirs = array_diff(glob($path.'*', GLOB_ONLYDIR),$d2);
        foreach($dirs as $d){
           removeEmptySubfolders($d);
        }
      
        if(count(array_diff(glob($path.'*'),$d2))===0){
          rmdir($path);
        }
      
      }
      

      经过测试,运行良好。 Windows 7 PHP 5.3.0 XAMPP

      【讨论】:

      • 好吧,当然,如果在递归调用之后检查文件夹是否为空,这将允许更有效的算法......真可惜:-|您对嵌套函数的问题也是正确的,PHP 在第二次调用外部函数时抛出致命错误。非常感谢!
      • 如果有答案,请打勾 =)
      • 完成。我不得不说,虽然 yu_sha 提出的代码似乎更好地回答了我在性能改进方面的问题,我会将其标记为可接受的答案,前提是代码中没有错误,因此可以复制粘贴。
      【解决方案6】:

      这一行

      $ret = $ret ? $ret : $res;
      

      可以变得更具可读性:

      $ret = $ret || $res;
      

      或者如果 PHP 有位运算符:

      $ret |= $res;
      

      【讨论】:

        猜你喜欢
        • 2015-04-24
        • 1970-01-01
        • 1970-01-01
        • 2010-12-07
        • 1970-01-01
        • 1970-01-01
        • 2012-02-23
        • 2012-07-21
        • 2020-07-23
        相关资源
        最近更新 更多