PHP 日本研討會 2024

scandir

(PHP 5, PHP 7, PHP 8)

scandir列出指定路徑內的檔案和目錄

說明

scandir(字串 $directory, 整數 $sorting_order = SCANDIR_SORT_ASCENDING, ?資源 $context = null): 陣列|false

directory 傳回檔案和目錄的 陣列

參數

directory

要掃描的目錄。

sorting_order

預設情況下,排序順序為字母升序。如果選用的 sorting_order 設為 SCANDIR_SORT_DESCENDING,則排序順序為字母降序。如果設為 SCANDIR_SORT_NONE,則結果為未排序。

context

關於 context 參數的說明,請參閱手冊的串流章節

傳回值

成功時傳回檔案名稱的 陣列,失敗時傳回 false。如果 directory 不是目錄,則傳回布林值 false,並產生 E_WARNING 層級的錯誤。

更新日誌

版本 說明
8.0.0 context 現在可以為 null。

範例

範例 #1 一個簡單的 scandir() 範例

<?php
$dir
= '/tmp';
$files1 = scandir($dir);
$files2 = scandir($dir, SCANDIR_SORT_DESCENDING);

print_r($files1);
print_r($files2);
?>

上面的範例會輸出類似下面的內容

Array
(
    [0] => .
    [1] => ..
    [2] => bar.php
    [3] => foo.txt
    [4] => somedir
)
Array
(
    [0] => somedir
    [1] => foo.txt
    [2] => bar.php
    [3] => ..
    [4] => .
)

注意

提示

如果啟用了 fopen 封裝器,則可以使用 URL 作為此函式的檔案名稱。有關如何指定檔案名稱的詳細資訊,請參閱 fopen()。請參閱支援的協定和封裝器,以取得有關各種封裝器具有哪些功能、其使用方式的注意事項以及它們可能提供的任何預定義變數的相關資訊連結。

參見

  • opendir() - 開啟目錄控制代碼
  • readdir() - 從目錄控制代碼讀取條目
  • glob() - 尋找符合模式的路徑名稱
  • is_dir() - 判斷檔案名稱是否為目錄
  • sort() - 以升序排序陣列

新增註解

使用者貢獻的註解 16 則註解

855
dwieeb at gmail dot com
12 年前
輕鬆擺脫 Linux 環境中 scandir() 拾取的點的方式

<?php
$directory
= '/path/to/my/directory';
$scanned_directory = array_diff(scandir($directory), array('..', '.'));
?>
176
mmda dot nl at gmail dot com
12 年前
這是我的拙見。我想要以遞迴方式建立目錄結構的陣列。我想要使用 foreach 輕鬆存取特定目錄中的資料。我提出了以下方法

<?php
function dirToArray($dir) {

$result = array();

$cdir = scandir($dir);
foreach (
$cdir as $key => $value)
{
if (!
in_array($value,array(".","..")))
{
if (
is_dir($dir . DIRECTORY_SEPARATOR . $value))
{
$result[$value] = dirToArray($dir . DIRECTORY_SEPARATOR . $value);
}
else
{
$result[] = $value;
}
}
}

return
$result;
}
?>

輸出
陣列
(
[subdir1] => 陣列
(
[0] => file1.txt
[subsubdir] => 陣列
(
[0] => file2.txt
[1] => file3.txt
)
)
[subdir2] => 陣列
(
[0] => file4.txt
}
)
29
info at remark dot no
6 年前
有人寫道,可以使用 array_slice 快速移除目錄條目「.」和「..」。但是,「-」是一個有效的條目,會排在這些條目之前,因此 array_slice 會移除錯誤的條目。
24
kodlee at kodleeshare dot net
12 年前
我需要找到一種方法來取得目錄及其所有子目錄中所有檔案的完整路徑。
這是我的解決方案:遞迴函式!

<?php
function find_all_files($dir)
{
$root = scandir($dir);
foreach(
$root as $value)
{
if(
$value === '.' || $value === '..') {continue;}
if(
is_file("$dir/$value")) {$result[]="$dir/$value";continue;}
foreach(
find_all_files("$dir/$value") as $value)
{
$result[]=$value;
}
}
return
$result;
}
?>
19
gambit_642 AT hotmailDOTcom
10 年前
需要一個可以遞迴或非遞迴地返回單個或多個目錄內容的東西,
適用於所有檔案或指定的副檔名,並且可以
從任何範圍或腳本輕鬆存取。

而且我希望允許重載,因為有時我太懶得傳遞所有參數。
<?php
class scanDir {
static private
$directories, $files, $ext_filter, $recursive;

// ----------------------------------------------------------------------------------------------
// scan(dirpath::string|array, extensions::string|array, recursive::true|false)
static public function scan(){
// 初始化預設值
self::$recursive = false;
self::$directories = array();
self::$files = array();
self::$ext_filter = false;

// 檢查是否有最小參數
if(!$args = func_get_args()){
die(
"必須提供路徑字串或路徑字串陣列");
}
if(
gettype($args[0]) != "string" && gettype($args[0]) != "array"){
die(
"必須提供路徑字串或路徑字串陣列");
}

// 檢查是否為遞迴掃描 | 預設動作:不包含子目錄
if(isset($args[2]) && $args[2] == true){self::$recursive = true;}

// 是否包含檔案副檔名篩選? | 預設動作:返回所有檔案類型
if(isset($args[1])){
if(
gettype($args[1]) == "array"){self::$ext_filter = array_map('strtolower', $args[1]);}
else
if(
gettype($args[1]) == "string"){self::$ext_filter[] = strtolower($args[1]);}
}

// 抓取路徑
self::verifyPaths($args[0]);
return
self::$files;
}

static private function
verifyPaths($paths){
$path_errors = array();
if(
gettype($paths) == "string"){$paths = array($paths);}

foreach(
$paths as $path){
if(
is_dir($path)){
self::$directories[] = $path;
$dirContents = self::find_contents($path);
} else {
$path_errors[] = $path;
}
}

if(
$path_errors){echo "以下目錄不存在<br />";die(var_dump($path_errors));}
}

// 這是我們掃描目錄的方式
static private function find_contents($dir){
$result = array();
$root = scandir($dir);
foreach(
$root as $value){
if(
$value === '.' || $value === '..') {continue;}
if(
is_file($dir.DIRECTORY_SEPARATOR.$value)){
if(!
self::$ext_filter || in_array(strtolower(pathinfo($dir.DIRECTORY_SEPARATOR.$value, PATHINFO_EXTENSION)), self::$ext_filter)){
self::$files[] = $result[] = $dir.DIRECTORY_SEPARATOR.$value;
}
continue;
}
if(
self::$recursive){
foreach(
self::find_contents($dir.DIRECTORY_SEPARATOR.$value) as $value) {
self::$files[] = $result[] = $value;
}
}
}
// 遞迴搜尋需要回傳
return $result;
}
}
?>

用法
scanDir::scan(路徑(s):字串|陣列, [檔案副檔名:字串|陣列], [子資料夾?:true|false]);
<?php
//掃描單個目錄的所有檔案,不包含子目錄
$files = scanDir::scan('D:\Websites\temp');

//掃描多個目錄的所有檔案,不包含子目錄
$dirs = array(
'D:\folder';
'D:\folder2';
'C:\Other';
);
$files = scanDir::scan($dirs);

// 掃描多個目錄中具有指定檔案副檔名的檔案,
// 不包含子目錄
$files = scanDir::scan($dirs, "jpg");
//或使用副檔名陣列
$file_ext = array(
"jpg",
"bmp",
"png"
);
$files = scanDir::scan($dirs, $file_ext);

// 掃描多個目錄中具有任何副檔名的檔案,
// 包含遞迴子資料夾中的檔案
$files = scanDir::scan($dirs, false, true);

// 多個目錄,具有指定的副檔名,包含子目錄中的檔案
$files = scanDir::scan($dirs, $file_ext, true);
?>
48
eep2004 at ukr dot net
10 年前
取得不含點的檔案列表的最快方法。
<?php
$files
= array_slice(scandir('/path/to/directory/'), 2);
5
artmanniako at gmail dot com
5 年前
我如何解決「.」和「..」的問題

$x = scandir__DIR__; //任何目錄
foreach ($x as $key => $value) {
if ('.' !== $value && '..' !== $value){
echo $value;
}
}
簡單且有效
5
coolbikram0 at gmail dot com
3 年前
一個簡單的遞迴函式,列出目錄中的所有檔案和子目錄
<?php
function listAllFiles($dir) {
$array = array_diff(scandir($dir), array('.', '..'));

foreach (
$array as &$item) {
$item = $dir . $item;
}
unset(
$item);
foreach (
$array as $item) {
if (
is_dir($item)) {
$array = array_merge($array, listAllFiles($item . DIRECTORY_SEPARATOR));
}
}
return
$array;
}
?>
2
csaba at alum dot mit dot edu
19 年前
加強版的 Scandir
適用於您想要篩選檔案清單,或者只想列出特定層級的子目錄時...

<?php
function dirList($path="", $types=2, $levels=1, $aFilter=array()) {
// 傳回指定檔案/目錄的陣列
// 在 $path 中開始搜尋 (預設為目前的工作目錄)
// 傳回 $types:2 => 檔案;1 => 目錄;3 => 兩者;
// $levels:1 => 僅在 $path 中搜尋;2 => $path 和所有子項目;
// 3 => $path、子項目、孫項目;0 => $path 和所有子目錄;
// 小於 0 => -$levels 的補數,或從 -$levels 開始的所有項目
// 例如:-1 => 除 $path 之外的所有項目;-2 => 除 $path + 子項目之外的所有後代
// 其餘參數是套用至完整路徑的正規表示式篩選陣列(清單)。
// 第一個字元 (正規表示式的 '/' 之前) '-' => NOT。
// 第一個字元 (在可能的 '-' 之後) 'd' => 套用至目錄名稱
// 篩選器可以字串陣列或字串清單的形式傳遞
// 請注意,輸出目錄會以 '*' 為前綴 (在傳回的上一行完成)
$dS = DIRECTORY_SEPARATOR;
if (!(
$path = realpath($path?$path:getcwd()))) return array(); // 路徑錯誤
// 下一行移除磁碟機上的結尾 \ (因為 PHP 中 c: == c:\ 而有效)。在 *nix 中是否正常?
if (substr($path,-1)==$dS) $path = substr($path,0,-1);
if (
is_null($types)) $types = 2;
if (
is_null($levels)) $levels = 1;
if (
is_null($aFilter)) $aFilter=array();

// 最後一個引數可以清單或陣列的形式傳遞
$aFilter = array_slice(func_get_args(),3);
if (
$aFilter && gettype($aFilter[0])=="array") $aFilter=$aFilter[0];
$adFilter = array();
// 現在將目錄篩選器移至個別陣列:
foreach ($aFilter as $i=>$filter) // 對於每個目錄篩選器...
if (($pos=stripos(" $filter","d")) && $pos<3) { // 下一行會消除 'd'
$adFilter[] = substr($filter,0,$pos-1) . substr($filter,$pos);
unset(
$aFilter[$i]); }
$aFilter = array_merge($aFilter); // 重設索引

$aRes = array(); // 結果,$aAcc 是累加器
$aDir = array($path); // 要檢查的目錄
for ($i=$levels>0?$levels++:-1;($aAcc=array())||$i--&&$aDir;$aDir=$aAcc)
while (
$dir = array_shift($aDir))
foreach (
scandir ($dir) as $fileOrDir)
if (
$fileOrDir!="." && $fileOrDir!="..") {
if (
$dirP = is_dir ($rp="$dir$dS$fileOrDir"))
if (
pathFilter("$rp$dS", $adFilter))
$aAcc[] = $rp;
if (
$i<$levels-1 && ($types & (2-$dirP)))
if (
pathFilter($rp, $aFilter))
$aRes[] = ($dirP?"*":"") . $rp; }
return
$aRes;
}
?>

使用範例
<?php
define
("_", NULL);
// 這將會尋找 c:\Photo 底下所有非 .jpg、非 .Thumbs.db 的檔案
$aFiles = dirList('c:\Photo', _, 0, '-/\.jpg$/i', '-/\\\\Thumbs.db$/');
$aFiles = dirList(); // 尋找目前目錄中的檔案
// 下一行會在非 Photo(s) 子目錄中尋找 .jpg 檔案,排除 Temporary Internet Files
set_time_limit(60); // 從頂層開始迭代可能需要一些時間
$aFiles = dirList("c:\\", _, 0, '/\.jpg$/i', '-d/\\\\Photos?$/i', '-d/Temporary Internet/i');
?>

請注意,如果掃描大型
目錄結構,此函數會耗費大量時間 (這也是使用 '[-]d/.../' 篩選器的原因)。

來自維也納的 Csaba Gabor
3
Pawel Dlugosz
19 年前
如果目錄包含像 -.jpg 這類檔案(舉例),scandir 的結果會有點「怪異」 ;)

<?php

$dir
= '/somedir';
$files = scandir($dir);
print_r($files);
?>

陣列
(
[0] => -.jpg
[1] => .
[2] => ..
[3] => foo.txt
[4] => somedir
)

注意 - 排序是依照 ASCII 順序 :)
3
Stan P. van de Burgt
20 年前
scandir() 結合正規表示式進行檔名比對,並根據 stat() 提供排序選項。

<?php
function myscandir($dir, $exp, $how='name', $desc=0)
{
$r = array();
$dh = @opendir($dir);
if (
$dh) {
while ((
$fname = readdir($dh)) !== false) {
if (
preg_match($exp, $fname)) {
$stat = stat("$dir/$fname");
$r[$fname] = ($how == 'name')? $fname: $stat[$how];
}
}
closedir($dh);
if (
$desc) {
arsort($r);
}
else {
asort($r);
}
}
return(
array_keys($r));
}

$r = myscandir('./book/', '/^article[0-9]{4}\.txt$/i', 'ctime', 1);
print_r($r);
?>

檔案可以依名稱和 stat() 屬性排序,可選擇升冪或降冪。

name 檔案名稱
dev 裝置號碼
ino inode 號碼
mode inode 保護模式
nlink 連結數量
uid 擁有者的使用者 ID
gid 擁有者的群組 ID
rdev 裝置類型,如果 inode 為裝置 *
size 大小,以位元組為單位
atime 最後存取時間 (Unix 時間戳記)
mtime 最後修改時間 (Unix 時間戳記)
ctime 最後 inode 變更時間 (Unix 時間戳記)
blksize 檔案系統 I/O 的區塊大小 *
blocks 已分配的區塊數量
1
simon dot riget at gmail dot com
8 年前
這是一個簡單且多用途的函式,它會回傳一個檔案陣列樹,並支援萬用字元比對。

<?php
// 列出樹狀結構中的檔案,並支援萬用字元 * 和 ?
function tree($path){
static
$match;

// 找出路徑中實際的目錄部分,並設定比對參數
$last=strrpos($path,"/");
if(!
is_dir($path)){
$match=substr($path,$last);
while(!
is_dir($path=substr($path,0,$last)) && $last!==false)
$last=strrpos($path,"/",-1);
}
if(empty(
$match)) $match="/*";
if(!
$path=realpath($path)) return;

// 列出檔案
foreach(glob($path.$match) as $file){
$list[]=substr($file,strrpos($file,"/")+1);
}

// 處理子目錄
foreach(glob("$path/*", GLOB_ONLYDIR) as $dir){
$list[substr($dir,strrpos($dir,"/",-1)+1)]=tree($dir);
}

return @
$list;
}
?>
1
fazle dot elahee at gmail dot com
12 年前
/**
* 這個函式將遞迴掃描子資料夾和資料夾中的所有檔案。
*
* @author Fazle Elahee
*
*/

function scanFileNameRecursivly($path = '', &$name = array() )
{
$path = $path == ''? dirname(__FILE__) : $path;
$lists = @scandir($path);

if(!empty($lists))
{
foreach($lists as $f)
{

if(is_dir($path.DIRECTORY_SEPARATOR.$f) && $f != ".." && $f != ".")
{
scanFileNameRecursivly($path.DIRECTORY_SEPARATOR.$f, &$name);
}
else
{
$name[] = $path.DIRECTORY_SEPARATOR.$f;
}
}
}
return $name;
}

$path = "/var/www/SimplejQueryDropdowns";
$file_names = scanFileNameRecursivly($path);

echo "<pre>";
var_dump($file_names);
echo "</pre>";
1
fatpratmatt dot at dot gmail dot com
16 年前
這個函式會產生選定目錄和所有子目錄中所有檔案的列表,並將它們放入一個非多維陣列中並回傳。

這個頁面上大多數的遞迴函式只會回傳一個多維陣列。

這實際上是修改自其他人的函式(感謝 mail at bartrail dot de ;])

<?php
function scanDirectories($rootDir, $allData=array()) {
// 如果需要,設定隱藏的檔名
$invisibleFileNames = array(".", "..", ".htaccess", ".htpasswd");
// 遍歷根目錄的內容
$dirContent = scandir($rootDir);
foreach(
$dirContent as $key => $content) {
// 過濾所有無法存取的檔案
$path = $rootDir.'/'.$content;
if(!
in_array($content, $invisibleFileNames)) {
// 如果內容是檔案且可讀取,則新增到陣列
if(is_file($path) && is_readable($path)) {
// 儲存包含路徑的檔名
$allData[] = $path;
// 如果內容是目錄且可讀取,則新增路徑和名稱
}elseif(is_dir($path) && is_readable($path)) {
// 遞迴回呼函式以開啟新的目錄
$allData = scanDirectories($path, $allData);
}
}
}
return
$allData;
}
?>

範例輸出

print_r(scanDirectories("www"));
---
陣列
(
[0] => www/index.php
[1] => www/admin.php
[3] => www/css/css.css
[4] => www/articles/2007/article1.txt
[4] => www/articles/2006/article1.txt
[8] => www/img/img1.png
)
0
SPekary
7 年前
除非您指定不排序,否則檔名會以 ASCII 字母順序排序,這表示數字在前,然後是大寫字母,然後是小寫字母,即使在檔案系統忽略檔名大小寫的作業系統上也是如此。

例如,在 Mac OS 上,當您的磁碟使用標準檔案系統格式化時,以下檔案將在 Finder 中以此順序顯示
1file.php
a.inc
B.txt
c.txt

然而,scandir 將產生以下順序的陣列
1file.php
B.txt
a.inc
c.txt
1
carneiro at isharelife dot com dot br
12 年前
<?php
/**
* 取得代表目錄樹的陣列
* @param string $directory 目錄路徑
* @param bool $recursive 是否包含子目錄
* @param bool $listDirs 是否在列表中包含目錄
* @param bool $listFiles 是否在列表中包含檔案
* @param regex $exclude 排除符合此正規表示式的路徑
*/
function directoryToArray($directory, $recursive = true, $listDirs = false, $listFiles = true, $exclude = '') {
$arrayItems = array();
$skipByExclude = false;
$handle = opendir($directory);
if (
$handle) {
while (
false !== ($file = readdir($handle))) {
preg_match("/(^(([\.]){1,2})$|(\.(svn|git|md))|(Thumbs\.db|\.DS_STORE))$/iu", $file, $skip);
if(
$exclude){
preg_match($exclude, $file, $skipByExclude);
}
if (!
$skip && !$skipByExclude) {
if (
is_dir($directory. DIRECTORY_SEPARATOR . $file)) {
if(
$recursive) {
$arrayItems = array_merge($arrayItems, directoryToArray($directory. DIRECTORY_SEPARATOR . $file, $recursive, $listDirs, $listFiles, $exclude));
}
if(
$listDirs){
$file = $directory . DIRECTORY_SEPARATOR . $file;
$arrayItems[] = $file;
}
} else {
if(
$listFiles){
$file = $directory . DIRECTORY_SEPARATOR . $file;
$arrayItems[] = $file;
}
}
}
}
closedir($handle);
}
return
$arrayItems;
}
?>
To Top