如何在PHP中加入文件系统路径字符串?

use*_*021 69 php string file

PHP中是否有内置函数来智能地连接路径字符串?给定"abc/de /"和"/fg/x.php"作为参数的函数应该返回"abc/de/fg/x.php"; 应使用"abc/de"和"fg/x.php"作为该函数的参数给出相同的结果.

如果没有,是否有可用的课程?它对于分割路径或移除部分路径也很有价值.如果您已经写过,可以在这里分享您的代码吗?

可以一直使用"/",我只编写Linux.

在Python中有os.path.join(),这很棒.

Ric*_*lli 114

function join_paths() {
    $paths = array();

    foreach (func_get_args() as $arg) {
        if ($arg !== '') { $paths[] = $arg; }
    }

    return preg_replace('#/+#','/',join('/', $paths));
}
Run Code Online (Sandbox Code Playgroud)

我的解决方案更简单,更类似于Python os.path.join的工作方式

考虑这些测试用例

array               my version    @deceze      @david_miller    @mark

['','']             ''            ''           '/'              '/'
['','/']            '/'           ''           '/'              '/'
['/','a']           '/a'          'a'          '//a'            '/a'
['/','/a']          '/a'          'a'          '//a'            '//a'
['abc','def']       'abc/def'     'abc/def'    'abc/def'        'abc/def'
['abc','/def']      'abc/def'     'abc/def'    'abc/def'        'abc//def'
['/abc','def']      '/abc/def'    'abc/def'    '/abc/def'       '/abc/def'
['','foo.jpg']      'foo.jpg'     'foo.jpg'    '/foo.jpg'       '/foo.jpg'
['dir','0','a.jpg'] 'dir/0/a.jpg' 'dir/a.jpg'  'dir/0/a.jpg'    'dir/0/a.txt'
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的答案,因为它最符合问题 - 它最接近`os.path.join`并且*智能地连接路径字符串*.答案可以通过添加`os.path.join`的"引用"实现来改进,并指出违反规则的OP特定(测试用例`['abc','/ def']`是错误的wrt`os .path.join`,但问题正确). (4认同)
  • @qix看,我明白你的意思,通常我同意你的意见,但实际上PHP不会在不使用斜杠作为路径分隔符的平台上运行,并且使用常量preg_replace()会变成一个真正的混乱(你需要逃避正则表达式中的路径分隔符),所以我选择这种权衡. (4认同)
  • 因此,即使PHP中的常量字符串插值几乎是免费的,你也会因为懒惰而偷工减料?啧啧. (4认同)
  • 作为一个注释,我的团队刚刚发现了一个`/`分隔符不起作用的情况(使用msys git shell在Windows Server 2012上安装旧的PHP5.3.4). (3认同)

dec*_*eze 49

由于这似乎是一个受欢迎的问题,而且评论中充满了"功能建议"或"错误报告"...所有这些代码片段都是用斜杠连接两个字符串而不重复它们之间的斜杠.就这样.不多也不少.它不会评估硬盘上的实际路径,也不会实际保留起始斜杠(如果需要,请将其添加回来,至少可以确保此代码始终返回字符串而不启动斜杠).

join('/', array(trim("abc/de/", '/'), trim("/fg/x.php", '/')));
Run Code Online (Sandbox Code Playgroud)

最终结果将始终是一个在开头或结尾没有斜杠且在其中没有双斜杠的路径.随意做一个功能.

编辑:这是一个很好的灵活的功能包装上面的代码片段.您可以根据需要传递任意数量的路径片段,作为数组或单独的参数:

function joinPaths() {
    $args = func_get_args();
    $paths = array();
    foreach ($args as $arg) {
        $paths = array_merge($paths, (array)$arg);
    }

    $paths = array_map(create_function('$p', 'return trim($p, "/");'), $paths);
    $paths = array_filter($paths);
    return join('/', $paths);
}

echo joinPaths(array('my/path', 'is', '/an/array'));
//or
echo joinPaths('my/paths/', '/are/', 'a/r/g/u/m/e/n/t/s/');
Run Code Online (Sandbox Code Playgroud)

:O)

  • 这应该使用DIRECTORY_SEPARATOR而不是'/'吗? (31认同)
  • function pj($ a,$ b){return rtrim($ a,'/').'/'.ltrim($ b,'/'); } (4认同)
  • 我不同意,因为该人明确告知他曾经使用python的'os.path.join`给出的结果给出了这个结果并且他发现了很好的结果.所以我不相信它是另一个功能.就像`join('/ a/b','../c')`应该返回`/ a/c`而不需要任何外部规范化. (3认同)
  • 这并不总是如所描述的那样起作用.joinPaths('','foo.jpg')变为'/foo.jpg'.在我的php文件管理器开始将用户上传的文件写入文件系统的根目录后,我注意到了这一点!更正后的版本应删除任何空字符串的路径. (2认同)
  • @fe_这与问题的问题完全不同. (2认同)
  • @fe_lix_*"作为参数给出"abc/de /"和"/fg/x.php"的函数应该返回"abc/de/fg/x.php";应该使用"abc /"给出相同的结果de"和"fg/x.php"作为该函数的参数."* - OP (2认同)
  • @fe_lix_是的.由于`os.path.join`只是一个脚注,这个答案解决了问题的大部分内容. (2认同)

Dav*_*ler 17

@deceze的函数不会保持领先/当试图加入以Unix绝对路径开头的路径时,例如joinPaths('/var/www', '/vhosts/site');.

function unix_path() {
  $args = func_get_args();
  $paths = array();

  foreach($args as $arg) {
    $paths = array_merge($paths, (array)$arg);
  }

  foreach($paths as &$path) {
    $path = trim($path, '/');
  }

  if (substr($args[0], 0, 1) == '/') {
    $paths[0] = '/' . $paths[0];
  }

  return join('/', $paths);
}
Run Code Online (Sandbox Code Playgroud)


mpe*_*pen 15

我的看法:

function trimds($s) {
    return rtrim($s,DIRECTORY_SEPARATOR);
}

function joinpaths() {
    return implode(DIRECTORY_SEPARATOR, array_map('trimds', func_get_args()));
}
Run Code Online (Sandbox Code Playgroud)

我已经使用了匿名函数trimds,但旧版本的PHP不支持它.

例:

join_paths('a','\\b','/c','d/','/e/','f.jpg'); // a\b\c\d\e\f.jpg (on Windows)
Run Code Online (Sandbox Code Playgroud)

更新 2013年4月 2014年3月 2018年5月:

function join_paths(...$paths) {
    return preg_replace('~[/\\\\]+~', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, $paths));
}
Run Code Online (Sandbox Code Playgroud)

这个将更正任何斜杠以匹配您的操作系统,不会删除前导斜杠,并清理连续多个斜杠.

  • 它总是创造一个绝对的路径,但至少有人提到DIRECTORY_SEPARATOR ...... (13认同)

Koa*_*ung 7

作为一个有趣的项目,我创建了另一个解决方案。对于所有操作系统应该是通用的。

对于 PHP 7.4+:

/**
 * Join string into a single URL string.
 *
 * @param string $parts,... The parts of the URL to join.
 * @return string The URL string.
 */
function join_paths(...$parts) {
    if (sizeof($parts) === 0) return '';
    $prefix = ($parts[0] === DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : '';
    $processed = array_filter(
        array_map(fn ($part) => rtrim($part, DIRECTORY_SEPARATOR), $parts),
        fn ($part) => !empty($part)
    );
    return $prefix . implode(DIRECTORY_SEPARATOR, $processed);
}
Run Code Online (Sandbox Code Playgroud)

对于 PHP 7.2+:

<?php

/**
 * Join string into a single URL string.
 *
 * @param string $parts,... The parts of the URL to join.
 * @return string The URL string.
 */
function join_paths(...$parts) {
    if (sizeof($parts) === 0) return '';
    $prefix = ($parts[0] === DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : '';
    $processed = array_filter(array_map(function ($part) {
        return rtrim($part, DIRECTORY_SEPARATOR);
    }, $parts), function ($part) {
        return !empty($part);
    });
    return $prefix . implode(DIRECTORY_SEPARATOR, $processed);
}
Run Code Online (Sandbox Code Playgroud)

对于 7.2 之前的 PHP 版本:

/**
 * Join string into a single URL string.
 *
 * @param string $parts,... The parts of the URL to join.
 * @return string The URL string.
 */
function join_paths() {
    $parts = func_get_args();
    if (sizeof($parts) === 0) return '';
    $prefix = ($parts[0] === DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : '';
    $processed = array_filter(array_map(function ($part) {
        return rtrim($part, DIRECTORY_SEPARATOR);
    }, $parts), function ($part) {
        return !empty($part);
    });
    return $prefix . implode(DIRECTORY_SEPARATOR, $processed);
}
Run Code Online (Sandbox Code Playgroud)

其行为的一些测试用例。

// relative paths
var_dump(join_paths('hello/', 'world'));
var_dump(join_paths('hello', 'world'));
var_dump(join_paths('hello', '', 'world'));
var_dump(join_paths('', 'hello/world'));
echo "\n";

// absolute paths
var_dump(join_paths('/hello/', 'world'));
var_dump(join_paths('/hello', 'world'));
var_dump(join_paths('/hello/', '', 'world'));
var_dump(join_paths('/hello', '', 'world'));
var_dump(join_paths('', '/hello/world'));
var_dump(join_paths('/', 'hello/world'));

Run Code Online (Sandbox Code Playgroud)

结果:

string(11) "hello/world"
string(11) "hello/world"
string(11) "hello/world"
string(11) "hello/world"

string(12) "/hello/world"
string(12) "/hello/world"
string(12) "/hello/world"
string(12) "/hello/world"
string(12) "/hello/world"
string(12) "/hello/world"
Run Code Online (Sandbox Code Playgroud)

更新:添加了支持PHP 7.2之前的版本。

更新:添加了带有箭头功能的 PHP 7.4+ 版本。


Chr*_*s J 6

另一种方法是使用implode()explode()

$a = '/a/bc/def/';
$b = '/q/rs/tuv/path.xml';

$path = implode('/',array_filter(explode('/', $a . $b)));

echo $path;  // -> a/bc/def/q/rs/tuv/path.xml
Run Code Online (Sandbox Code Playgroud)


Geo*_*und 6

如果知道文件/目录存在,则可以添加额外的斜杠(可能是不必要的),然后调用realpath,即

realpath(join('/', $parts));

当然,这与Python版本不太一样,但是在许多情况下可能已经足够了。


Sam*_*zon 5

受 Python 启发的优雅 PHP 单行方式加入路径。

这段代码没有使用不必要的数组。

多平台

function os_path_join(...$parts) {
  return preg_replace('#'.DIRECTORY_SEPARATOR.'+#', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, array_filter($parts)));
}
Run Code Online (Sandbox Code Playgroud)

基于 Unix 的系统

function os_path_join(...$parts) {
  return preg_replace('#/+#', '/', implode('/', array_filter($parts)));
}
Run Code Online (Sandbox Code Playgroud)

没有 REST 参数的基于 Unix 的系统(不尊重明确的 PEP8 哲学):

function os_path_join() {
  return preg_replace('#/+#', '/', implode('/', array_filter(func_get_args())));
}
Run Code Online (Sandbox Code Playgroud)

用法

$path = os_path_join("", "/", "mydir/", "/here/");
Run Code Online (Sandbox Code Playgroud)

额外奖励:如果你想真正遵循 Python os.path.join()。第一个参数是必需的:

function os_path_join($path=null, ...$paths) {
  if (!is_null($path)) {
    throw new Exception("TypeError: join() missing 1 required positional argument: 'path'", 1);
  }
  $path = rtrim($path, DIRECTORY_SEPARATOR);
  foreach ($paths as $key => $current_path) {
    $paths[$key] = $paths[$key] = trim($current_path, DIRECTORY_SEPARATOR);
  }
  return implode(DIRECTORY_SEPARATOR, array_merge([$path], array_filter($paths)));
}
Run Code Online (Sandbox Code Playgroud)

如果需要,请检查 os.path.join() 源:https://github.com/python/cpython/blob/master/Lib/ntpath.py

警告:此解决方案不适合 url。