Is there a better way to check if a dir is empty than parsing it?
Don't think so. Shortest/quickest way I can think of is the following, which should work as far as I can see.
function dir_is_empty($path)
{
$empty = true;
$dir = opendir($path);
while($file = readdir($dir))
{
if($file != '.' && $file != '..')
{
$empty = false;
break;
}
}
closedir($dir);
return $empty;
}
This should only go through a maximum of 3 files. The two . and .. and potentially whatever comes next. If something comes next, it's not empty, and if not, well then it's empty.
Not really, but you can try to delete it. If it fails, its not empty (or you just can't delete it ;))
function dirIsEmpty ($dir) {
return rmdir($dir) && mkdir($dir);
}
Update:
It seems, that the answer, that takes the condition "without parsing" into account, doesn't find much friends ;)
function dirIsEmpty ($dir) {
return count(glob("$dir/**/*")) === 0:
}
Note, that this assumes, that the directory and every subdirectory doesn't contain any hidden file (starting with a single .).
Related
So I have a code snippet that reads a directory and performs certain actions on the files inside. I have an array of filenames to exclude. My code looks like this:
$excluded = array(".","..","thumbs.db");
if($fh = #opendir($dir))
{
while(false !== ($file = #readdir($fh)))
{
if(in_array(strtolower($file),$excluded))
{
continue;
}
//do processing here...
Now, I want it that zip files also should be excluded. Since I do not know what name they might exist in, I will need to skip them based on extension.
Now I know I can split the filename and look at the last element to see if it zip etc, but what I wanted to ask is, is there a way to achieve it within the constraints of what is coded already - like adding it like this, and then tweaking the loop to handle it...
$excluded = array(".","..","thumbs.db","*.zip");
This should do the trick:
$excluded = array(".","..","thumbs.db");
$excludedExtensions = array(".zip",".rar");
if($fh = #opendir($dir))
{
while(false !== ($file = #readdir($fh)))
{
if(in_array(strtolower($file),$excluded) ||
in_array(strtolower(substr($file, -4)), $excludedExtensions) )
{
continue;
}
//do processing here...
It's not exactly what you're looking for, but i don't think it's possible to do the way you wanted it to :(
-------------------------------------------------------------------------------------
EDIT
I wanted to make a more reliable way to do this, since there is some files which have 4 or even 5 letters in their extension. After looking though the PHP manual, i found this:
$excluded = array(".","..","thumbs.db");
$excludedExtensions = array(".zip",".rar", ".7z", ".jpeg", ".phtml");
if($fh = #opendir($dir))
{
while(false !== ($file = #readdir($fh)))
{
$path_parts = pathinfo($file);
if(in_array(strtolower($file),$excluded) ||
in_array(strtolower($path_parts['extension'])) )
{
continue;
}
//do processing here...
See more here: PHP manual: pathinfo
I want to grab the first file in a directory, without touching/grabbing all the other files. The filename is unknown.
One very short way could be this, using glob:
$file = array_slice(glob('/directory/*.jpg'), 0, 1);
But if there are a lot of files in that directory, there will be some overhead.
Other ways are answers to this question - but all involve a loop and are also longer then the glob example:
PHP: How can I grab a single file from a directory without scanning entire directory?
Is there a very short and efficient way to solve this?
Probably not totally efficient, but if you only want the FIRST jpg that appears, then
$dh = opendir('directory/');
while($filename = readdir($dh)) {
if (substr($filename, -4) == '.jpg')) {
break;
}
}
Well this is not totally a one-liner, but it is a way to go I believe:
$result = null;
foreach(new FilesystemIterator('directory/') as $file)
{
if($file->isFile() && $file->getExtension() == 'jpg') {
$result = $file->getPathname();
break;
}
}
but why don't you wrap it in a function and use it like get_first_file('directory/') ? It will be a nice and short!
This function will get the first filename of any type.
function get_first_filename ($dir) {
$d = dir($dir);
while ($f = $d->read()){
if (is_file($dir . '/' . $f)) {
$d->close();
return $f;
}
}
}
I have a directory with 1.3 Million files that I need to move into a database. I just need to grab a single filename from the directory WITHOUT scanning the whole directory. It does not matter which file I grab as I will delete it when I am done with it and then move on to the next. Is this possible? All the examples I can find seem to scan the whole directory listing into an array. I only need to grab one at a time for processing... not 1.3 Million every time.
This should do it:
<?php
$h = opendir('./'); //Open the current directory
while (false !== ($entry = readdir($h))) {
if($entry != '.' && $entry != '..') { //Skips over . and ..
echo $entry; //Do whatever you need to do with the file
break; //Exit the loop so no more files are read
}
}
?>
readdir
Returns the name of the next entry in the directory. The entries are returned in the order in which they are stored by the filesystem.
Just obtain the directories iterator and look for the first entry that is a file:
foreach(new DirectoryIterator('.') as $file)
{
if ($file->isFile()) {
echo $file, "\n";
break;
}
}
This also ensures that your code is executed on some other file-system behaviour than the one you expect.
See DirectoryIterator and SplFileInfo.
readdir will do the trick. Check the exampl on that page but instead of doing the readdir call in the loop, just do it once. You'll get the first file in the directory.
Note: you might get ".", "..", and other similar responses depending on the server, so you might want to at least loop until you get a valid file.
do you want return first directory OR first file? both? use this:
create function "pickfirst" with 2 argument (address and mode dir or file?)
function pickfirst($address,$file) { // $file=false >> pick first dir , $file=true >> pick first file
$h = opendir($address);
while (false !== ($entry = readdir($h))) {
if($entry != '.' && $entry != '..' && ( ($file==false && !is_file($address.$entry)) || ($file==true && is_file($address.$entry)) ) )
{ return $entry; break; }
} // end while
} // end function
if you want pick first directory in your address set $file to false and if you want pick first file in your address set $file to true.
good luck :)
I'm building a file browser, and I need to know if a directory has children (but not how many or what type).
What's the most efficient way to find if a directory has children? glob()? scandir() it? Check its tax records?
Edit
It seems I was misunderstood, although I thought I was pretty clear. I'll try to restate my question.
What is the most efficient way to know if a directory is not empty? I'm basically looking for a boolean answer - NOT EMPTY or EMPTY.
I don't need to know:
how many files are in the directory
what the files are
when they were modified
etc.
I do need to know:
does the directory have any files in it at all
efficiently.
I think this is very efficient:
function dir_contains_children($dir) {
$result = false;
if($dh = opendir($dir)) {
while(!$result && ($file = readdir($dh)) !== false) {
$result = $file !== "." && $file !== "..";
}
closedir($dh);
}
return $result;
}
It stops the listing of the directories contents as soon as there is a file or directory found (not including the . and ..).
You could use 'find' to list all empty directories in one step:
exec("find '$dir' -maxdepth 1 -empty -type d",$out,$ret);
print_r($out);
Its not "pure" php but its simple and fast.
This should do, easy, quick and effective.
<?php
function dir_is_empty($dir) {
$dirItems = count(scandir($dir));
if($dirItems > 2) return false;
else return true;
}
?>
Unfortunately, each solution so far has lacked the brevity and elegance necessary to shine above the rest.
So, I was forced to homebrew a solution myself, which I'll be implementing until something better pops up:
if(count(glob($dir."/*")) {
echo "NOT EMPTY";
}
Still not sure of the efficiency of this compared to other methods, which was the original question.
I wanted to expand vstm's answer - Check only for child directories (and not files):
/**
* Check if directory contains child directories.
*/
function dir_contains_children_dirs($dir) {
$result = false;
if($dh = opendir($dir)) {
while (!$result && ($file = readdir($dh))) {
$result = $file !== "." && $file !== ".." && is_dir($dir.'/'.$file);
}
closedir($dh);
}
return $result;
}
I have a function that detects all files started by a string and it returns an array filled with the correspondent files, but it is starting to get slow, because I have arround 20000 files in a particular directory.
I need to optimize this function, but I just can't see how. This is the function:
function DetectPrefix ($filePath, $prefix)
{
$dh = opendir($filePath);
while (false !== ($filename = readdir($dh)))
{
$posIni = strpos( $filename, $prefix);
if ($posIni===0):
$files[] = $filename;
endif;
}
if (count($files)>0){
return $files;
} else {
return null;
}
}
What more can I do?
Thanks
http://php.net/glob
$files = glob('/file/path/prefix*');
Wikipedia breaks uploads up by the first couple letters of their filenames, so excelfile.xls would go in a directory like /uploads/e/x while textfile.txt would go in /uploads/t/e.
Not only does this reduce the number of files glob (or any other approach) has to sort through, but it avoids the maximum files in a directory issue others have mentioned.
You could use scandir() to list the files in the directory, instead of iterating through them one-by-one using readdir(). scandir() returns an array of the files.
However, it'd be better if you could change your file system organization - do you really need to store 20000+ files in a single directory?
As the other answers mention, I'd look at glob(), scandir(), and/or the DirectoryIterator class, there is no need to recreate the wheel.
However watch out! check your operating system, but there may be a limit on the maximum number of files in a single directory. If this is the case and you just keep adding files in the same directory you will have some downtime, and some problems, when you reach the limit. This error will probably appear as a permissions or write failure and not an obvious "you can't write more files in a single directory" message.
I'm not sure but probably DirectoryIterator is a bit faster. Also add caching so that list gets generated only when files are added or deleted.
You just need to compare the first length of prefix characters. So try this:
function DetectPrefix($filePath, $prefix) {
$dh = opendir($filePath);
$len = strlen($prefix);
$files = array();
while (false !== ($filename = readdir($dh))) {
if (substr($filename, 0, $len) === $prefix) {
$files[] = $filename;
}
}
if (count($files)) {
return $files;
} else {
return null;
}
}