简体   繁体   English

来自绝对路径和给定基本路径的相对文件名

[英]Relative filename from absolute and given base path

How to get relative filename from absolute one and some given path? 如何从绝对一个和某些给定路径获取相对文件名?

For example: 例如:

foo('/a/b/c/1.txt', '/a/d/e');    // '../../b/c/1.txt'
foo('/a/b/../d/1.txt', '/a/d/e'); // '../c/1.txt'

Is there some native function for this? 是否有一些本机功能?

My thoughts, if there is not: 我的想法,如果没有:

  • Normalize both params: need to use some realpath replacement, because files can to not exist. 标准化两个参数:需要使用一些realpath替换,因为文件可能不存在。 example
  • Cut common parts from both 削减双方共同的部分
  • add rest parts from $basepath as '..' 将$ basepath中的其余部分添加为“ ..”

Manual way looks too heavy for that common task.. 手动方式对于该常见任务来说太重了。

For now, I assume there is not any native implementation and want to share my implementation of this. 现在,我假设没有任何本机实现,并希望共享我的实现。

/**
 * realpath analog without filesystem check
 */
function str_normalize_path(string $path, string $sep = DIRECTORY_SEPARATOR): string {
    $parts = array_filter(explode($sep, $path));
    $stack = [];
    foreach ($parts as $part) {
        switch($part) {
            case '.': break;
            case '..': array_pop($stack); break; // excess '..' just ignored by array_pop([]) silency
            default: array_push($stack, $part);
        }
    }

    return implode($sep, $stack);
}

function str_relative_path(string $absolute, string $base, string $sep = DIRECTORY_SEPARATOR) {
    $absolute = str_normalize_path($absolute);
    $base = str_normalize_path($base);
    // find common prefix
    $prefix_len = 0;
    for ($i = 0; $i < min(mb_strlen($absolute), mb_strlen($base)); ++$i) {
        if ($absolute[$i] !== $base[$i]) break;
        $prefix_len++;
    }
    // cut common prefix
    if ($prefix_len > 0) {
        $absolute = mb_substr($absolute, $prefix_len);
        $base = mb_substr($base, $prefix_len);
    }
    // put '..'s for exit to closest common path
    $base_length = count(explode($sep, $base));
    $relative_parts = explode($sep, $absolute);
    while($base_length-->0) array_unshift($relative_parts, '..');
    return implode($sep, $relative_parts);
}

$abs = '/a/b/../fk1/fk2/../.././d//proj42/1.txt';
$base = '/a/d/fk/../e/f';

echo str_relative_path($abs, $base); // ../../proj42/1.txt

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM