views:

247

answers:

3

I need to extract the name of the direct sub directory from a full path string.

For example, say we have:

$str = "dir1/dir2/dir3/dir4/filename.ext";
$dir = "dir1/dir2";

Then the name of the sub-directory in the $str path relative to $dir would be "dir3". Note that $dir never has '/' at the ends.

So the function should be:

$subdir = getsubdir($str,$dir);
echo $subdir; // Outputs "dir3"

If $dir="dir1" then the output would be "dir2". If $dir="dir1/dir2/dir3/dir4" then the output would be "" (empty). If $dir="" then the output would be "dir1". Etc..

Currently this is what I have, and it works (as far as I've tested it). I'm just wondering if there's a simpler way since I find I'm using a lot of string functions. Maybe there's some magic regexp to do this in one line? (I'm not too good with regexp unfortunately).

function getsubdir($str,$dir) {
    // Remove the filename
    $str = dirname($str);

    // Remove the $dir
    if(!empty($dir)){
        $str = str_replace($dir,"",$str);
    }

    // Remove the leading '/' if there is one
    $si = stripos($str,"/");
    if($si == 0){
        $str = substr($str,1);
    }

    // Remove everything after the subdir (if there is anything)
    $lastpart = strchr($str,"/");
    $str = str_replace($lastpart,"",$str);

    return $str;
}

As you can see, it's a little hacky in order to handle some odd cases (no '/' in input, empty input, etc). I hope all that made sense. Any help/suggestions are welcome.

Update (altered solution):

Well Alix Axel had it spot on. Here's his solution with slight tweaks so that it matches my exact requirements (eg: it must return a string, only directories should be outputted (not files))

function getsubdir($str,$dir) {
    $str = dirname($str);
    $temp = array_slice(array_diff(explode('/', $str), explode('/', $dir)), 0, 1);
    return $temp[0];   
}
+2  A: 

How about splitting the whole thing into an array:

$fullpath = explode("/", "dir1/dir2/dir3/dir4/filename.ext");
$fulldir  = explode("/", "dir1/dir2");

// Will result in array("dir1","dir2","dir3", "dir4", "filename.ext");
// and            array("dir1", "dir2");

you should then be able to use array_diff():

$remainder = array_diff($fullpath, $fulldir);

// Should return array("dir3", "dir4", "filename.ext");

then, getting the direct child is easy:

echo $remainder[0];

I can't test this right now but it should work.

Pekka
+3  A: 

Here you go:

function getSubDir($dir, $sub)
{
    return array_slice(array_diff(explode('/', $dir), explode('/', $sub)), 0, 1);
}

EDIT - Foolproof implementation:

function getSubDirFoolproof($dir, $sub)
{
    /*
    This is the ONLY WAY we have to make SURE that the
    last segment of $dir is a file and not a directory.
    */
    if (is_file($dir))
    {
        $dir = dirname($dir);
    }

    // Is it necessary to convert to the fully expanded path?
    $dir = realpath($dir);
    $sub = realpath($sub);

    // Do we need to worry about Windows?
    $dir = str_replace('\\', '/', $dir);
    $sub = str_replace('\\', '/', $sub);

    // Here we filter leading, trailing and consecutive slashes.
    $dir = array_filter(explode('/', $dir));
    $sub = array_filter(explode('/', $sub));

    // All done!
    return array_slice(array_diff($dir, $sub), 0, 1);
}
Alix Axel
Well this has to get a +1 for brevity. :)
Pekka
@Pekka: Your answer is much more descriptive, I'll upvote it but I've to "come back in about 3 hours". :P
Alix Axel
Brilliant! You guys are so quick! This works perfectly except for a few specific things which I'll post in the original message.
Nebs
@Nebs: Check my update.
Alix Axel
@Alix: Thanks for the update.
Nebs
@Nebs: No problem. =)
Alix Axel
A: 

Here's a similar "short" solution, this time using string functions rather than array functions. If there is no corresponding part to be gotten from the string, getsubdir will return FALSE. The strtr segment is a quick way to escape the percents, which have special meaning to sscanf.

function getsubdir($str, $dir) {
    return sscanf($str, strtr($dir, '%', '%%').'/%[^/]', $name) === 1 ? $name : FALSE;
}

And a quick test so you can see how it behaves:

$str = "dir1/dir2/dir3/dir4/filename.ext";
var_dump(
    getSubDir($str, "dir1"),
    getSubDir($str, "dir1/dir2/dir3"),
    getSubDir($str, "cake")
);
// string(4) "dir2"
// string(4) "dir4"
// bool(false)
salathe