PHP中是否有内置函数来智能地连接路径字符串?给定abc/de/
和/fg/x.php
作为参数的函数应返回abc/de/fg/x.php
; 应该使用abc/de
和fg/x.php
作为该函数的参数给出相同的结果。
如果没有,是否有可用的课程?这对于拆分路径或删除其中的一部分可能也很有价值。如果您写过什么,可以在这里分享您的代码吗?
可以始终使用/
,我只为Linux编码。
在Python中有os.path.join
,这很棒。
PHP中是否有内置函数来智能地连接路径字符串?给定abc/de/
和/fg/x.php
作为参数的函数应返回abc/de/fg/x.php
; 应该使用abc/de
和fg/x.php
作为该函数的参数给出相同的结果。
如果没有,是否有可用的课程?这对于拆分路径或删除其中的一部分可能也很有价值。如果您写过什么,可以在这里分享您的代码吗?
可以始终使用/
,我只为Linux编码。
在Python中有os.path.join
,这很棒。
os.path.join('some/relative/path, '/an/absolute/path')
它将始终返回/an/absolute/path
。因此,您正在寻找os.path.join
替代品(然后修复您的示例)或与其接近的替代品,除了将第二(或第n)个绝对路径视为相对路径。
os.path.join
。阅读:“给定abc/de/
并/fg/x.php
作为参数的函数应返回abc/de/fg/x.php
”。
Answers:
由于这似乎是一个很受欢迎的问题,并且注释中充斥着“功能建议”或“错误报告” ...此代码段所做的只是将两个带有斜杠的字符串连接在一起,而不必在它们之间重复斜杠。就这样。不多不少。它不评估硬盘上的实际路径,也不实际保留开始的斜杠(如果需要,可以将其添加回去,至少可以确保此代码始终返回字符串而不开始斜杠)。
join('/', array(trim("abc/de/", '/'), trim("/fg/x.php", '/')));
最终结果将始终是在开头或结尾没有斜杠且在其中没有双斜杠的路径。随意发挥作用。
编辑:这是上面代码段的一个很好的灵活函数包装器。您可以根据需要传递任意数量的路径摘要,可以是数组或单独的参数:
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/');
:o)
os.path.join
给出的结果,该结果给出了这个结果,并且他觉得很棒。因此,我不认为这是另一个功能。就像join('/a/b','../c')
返回时一样,/a/c
不需要任何外部规范化。
function join_paths() {
$paths = array();
foreach (func_get_args() as $arg) {
if ($arg !== '') { $paths[] = $arg; }
}
return preg_replace('#/+#','/',join('/', $paths));
}
我的解决方案更简单,更类似于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'
os.path.join
并且可以智能地联接路径字符串。可以通过添加的“引用”实现os.path.join
并指示违反规则的OP的具体细节来改进答案(测试用例['abc','/def']
是错误的wrt os.path.join
,但根据问题正确)。
/
分隔符不起作用的情况(使用msys git shell在Windows Server 2012上安装了旧的PHP5.3.4)。
尝试加入以Unix绝对路径开头的路径时,例如@deceze的功能不会保留前导/ 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);
}
我的看法:
function trimds($s) {
return rtrim($s,DIRECTORY_SEPARATOR);
}
function joinpaths() {
return implode(DIRECTORY_SEPARATOR, array_map('trimds', func_get_args()));
}
我本来可以使用匿名函数 trimds
,但是旧版本的PHP不支持它。
例:
join_paths('a','\\b','/c','d/','/e/','f.jpg'); // a\b\c\d\e\f.jpg (on Windows)
更新 2013年4月 2014年3月 2018年5月:
function join_paths(...$paths) {
return preg_replace('~[/\\\\]+~', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, $paths));
}
这将纠正任何斜杠以匹配您的操作系统,不会删除前导斜杠,并且连续清理和清除多个斜杠。
下面的解决方案使用@RiccardoGalli提出的逻辑,但经过改进以利用DIRECTORY_SEPARATOR
常量本身,如@Qix和@FélixSaparelli所建议的那样,更重要的是,修剪每个给定的元素以避免在最终的目录中出现仅空格的文件夹名称路径(在我的情况下这是必需条件)。
关于preg_replace()
模式中目录分隔符的转义,如您所见,我使用了preg_quote()
功能良好的函数。
此外,我将只替换多个分隔符(RegExp量化器{2,}
)。
// PHP 7.+
function paths_join(string ...$parts): string {
$parts = array_map('trim', $parts);
$path = [];
foreach ($parts as $part) {
if ($part !== '') {
$path[] = $part;
}
}
$path = implode(DIRECTORY_SEPARATOR, $path);
return preg_replace(
'#' . preg_quote(DIRECTORY_SEPARATOR) . '{2,}#',
DIRECTORY_SEPARATOR,
$path
);
}
要获取路径的一部分,可以使用pathinfo http://nz2.php.net/manual/en/function.pathinfo.php
加入@deceze的响应看起来不错
这是deceze发布的功能的更正版本。没有此更改,joinPaths('','foo.jpg')变为'/foo.jpg'
function joinPaths() {
$args = func_get_args();
$paths = array();
foreach ($args as $arg)
$paths = array_merge($paths, (array)$arg);
$paths2 = array();
foreach ($paths as $i=>$path)
{ $path = trim($path, '/');
if (strlen($path))
$paths2[]= $path;
}
$result = join('/', $paths2); // If first element of old path was absolute, make this one absolute also
if (strlen($paths[0]) && substr($paths[0], 0, 1) == '/')
return '/'.$result;
return $result;
}
与操作系统无关的版本,基于mpen的答案,但封装为单个函数,并可以选择添加尾随路径分隔符。
function joinPathParts($parts, $trailingSeparator = false){
return implode(
DIRECTORY_SEPARATOR,
array_map(
function($s){
return rtrim($s,DIRECTORY_SEPARATOR);
},
$parts)
)
.($trailingSeparator ? DIRECTORY_SEPARATOR : '');
}
或为您的单线恋人:
function joinPathParts($parts, $trailingSeparator = false){
return implode(DIRECTORY_SEPARATOR, array_map(function($s){return rtrim($s,DIRECTORY_SEPARATOR);}, $parts)).($trailingSeparator ? DIRECTORY_SEPARATOR : '');
}
只需使用一系列路径部分来调用它:
// No trailing separator - ex. C:\www\logs\myscript.txt
$logFile = joinPathParts([getcwd(), 'logs', 'myscript.txt']);
// Trailing separator - ex. C:\www\download\images\user1234\
$dir = joinPathParts([getcwd(), 'download', 'images', 'user1234'], true);
优雅的Python启发式PHP一线式加入路径。
此代码不使用不必要的数组。
function os_path_join(...$parts) {
return preg_replace('#'.DIRECTORY_SEPARATOR.'+#', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, array_filter($parts)));
}
function os_path_join(...$parts) {
return preg_replace('#/+#', '/', implode('/', array_filter($parts)));
}
function os_path_join() {
return preg_replace('#/+#', '/', implode('/', array_filter(func_get_args())));
}
$path = os_path_join("", "/", "mydir/", "/here/");
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)));
}
如果需要,请检查os.path.join()源:https : //github.com/python/cpython/blob/master/Lib/ntpath.py
警告:此解决方案不适用于网址。
作为一个有趣的项目,我创建了另一个解决方案。对于所有操作系统应通用:
<?php
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);
}
// 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'));
结果:
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"
这是一个行为类似于Node的path.resolve
函数:
function resolve_path() {
$working_dir = getcwd();
foreach(func_get_args() as $p) {
if($p === null || $p === '') continue;
elseif($p[0] === '/') $working_dir = $p;
else $working_dir .= "/$p";
}
$working_dir = preg_replace('~/{2,}~','/', $working_dir);
if($working_dir === '/') return '/';
$out = [];
foreach(explode('/',rtrim($working_dir,'/')) as $p) {
if($p === '.') continue;
if($p === '..') array_pop($out);
else $out[] = $p;
}
return implode('/',$out);
}
测试用例:
resolve_path('/foo/bar','./baz') # /foo/bar/baz
resolve_path('/foo/bar','/tmp/file/') # /tmp/file
resolve_path('/foo/bar','/tmp','file') # /tmp/file
resolve_path('/foo//bar/../baz') # /foo/baz
resolve_path('/','foo') # /foo
resolve_path('/','foo','/') # /
resolve_path('wwwroot', 'static_files/png/', '../gif/image.gif')
# __DIR__.'/wwwroot/static_files/gif/image.gif'
从里卡多·加里(Ricardo Galli)的出色回答中可以避免避免取消协议前缀的一些改进。
这个想法是在一个参数中测试协议的存在,并将其保留到结果中。警告:这是一个幼稚的实现!
例如:
array("http://domain.de","/a","/b/")
结果到(保存协议)
"http://domain.de/a/b/"
而不是(杀死协议)
"http:/domain.de/a/b/"
但是http://codepad.org/hzpWmpzk需要更好的代码编写技能。
我喜欢里卡多的答案,我认为这是最好的答案。
我正在使用它来连接URL构建中的路径,但是做了一个小的改动以处理协议的双斜杠:
function joinPath () {
$paths = array();
foreach (func_get_args() as $arg) {
if ($arg !== '') { $paths[] = $arg; }
}
// Replace the slash with DIRECTORY_SEPARATOR
$paths = preg_replace('#/+#', '/', join('/', $paths));
return preg_replace('#:/#', '://', $paths);
}
function path_combine($paths) {
for ($i = 0; $i < count($paths); ++$i) {
$paths[$i] = trim($paths[$i]);
}
$dirty_paths = explode(DIRECTORY_SEPARATOR, join(DIRECTORY_SEPARATOR, $paths));
for ($i = 0; $i < count($dirty_paths); ++$i) {
$dirty_paths[$i] = trim($dirty_paths[$i]);
}
$unslashed_paths = array();
for ($i = 0; $i < count($dirty_paths); ++$i) {
$path = $dirty_paths[$i];
if (strlen($path) == 0) continue;
array_push($unslashed_paths, $path);
}
$first_not_empty_index = 0;
while(strlen($paths[$first_not_empty_index]) == 0) {
++$first_not_empty_index;
}
$starts_with_slash = $paths[$first_not_empty_index][0] == DIRECTORY_SEPARATOR;
return $starts_with_slash
? DIRECTORY_SEPARATOR . join(DIRECTORY_SEPARATOR, $unslashed_paths)
: join(DIRECTORY_SEPARATOR, $unslashed_paths);
}
用法示例:
$test = path_combine([' ', '/cosecheamo', 'pizze', '///// 4formaggi', 'GORGONZOLA']);
echo $test;
将输出:
/cosecheamo/pizze/4formaggi/GORGONZOLA
这是我的解决方案:
function joinPath(): string {
$path = '';
foreach (func_get_args() as $numArg => $arg) {
$arg = trim($arg);
$firstChar = substr($arg, 0, 1);
$lastChar = substr($arg, -1);
if ($numArg != 0 && $firstChar != '/') {
$arg = '/'.$arg;
}
# Eliminamos el slash del final
if ($lastChar == '/') {
$arg = rtrim($arg, '/');
}
$path .= $arg;
}
return $path;
}
嗯,大多数似乎有点复杂。邓诺,这是我的看法:
// Takes any amount of arguments, joins them, then replaces double slashes
function join_urls() {
$parts = func_get_args();
$url_part = implode("/", $parts);
return preg_replace('/\/{1,}/', '/', $url_part);
}
对于想要使用Windows反斜杠和Linux正斜杠的联接功能的用户。
用法:
<?php
use App\Util\Paths
echo Paths::join('a','b'); //Prints 'a/b' on *nix, or 'a\\b' on Windows
类文件:
<?php
namespace App\Util;
class Paths
{
public static function join_with_separator($separator, $paths) {
$slash_delimited_path = preg_replace('#\\\\#','/', join('/', $paths));
$duplicates_cleaned_path = preg_replace('#/+#', $separator, $slash_delimited_path);
return $duplicates_cleaned_path;
}
public static function join() {
$paths = array();
foreach (func_get_args() as $arg) {
if ($arg !== '') { $paths[] = $arg; }
}
return Paths::join_with_separator(DIRECTORY_SEPARATOR, $paths);
}
}
这是测试功能:
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Util\Paths;
class PathsTest extends TestCase
{
public function testWindowsPaths()
{
$TEST_INPUTS = [
[],
['a'],
['a','b'],
['C:\\','blah.txt'],
['C:\\subdir','blah.txt'],
['C:\\subdir\\','blah.txt'],
['C:\\subdir','nested','1/2','blah.txt'],
];
$EXPECTED_OUTPUTS = [
'',
'a',
'a\\b',
'C:\\blah.txt',
'C:\\subdir\\blah.txt',
'C:\\subdir\\blah.txt',
'C:\\subdir\\nested\\1\\2\\blah.txt',
];
for ($i = 0; $i < count($TEST_INPUTS); $i++) {
$actualPath = Paths::join_with_separator('\\', $TEST_INPUTS[$i]);
$expectedPath = $EXPECTED_OUTPUTS[$i];
$this->assertEquals($expectedPath, $actualPath);
}
}
public function testNixPaths()
{
$TEST_INPUTS = [
[],
['a'],
['a','b'],
['/home','blah.txt'],
['/home/username','blah.txt'],
['/home/username/','blah.txt'],
['/home/subdir','nested','1\\2','blah.txt'],
];
$EXPECTED_OUTPUTS = [
'',
'a',
'a/b',
'/home/blah.txt',
'/home/username/blah.txt',
'/home/username/blah.txt',
'/home/subdir/nested/1/2/blah.txt',
];
for ($i = 0; $i < count($TEST_INPUTS); $i++) {
$actualPath = Paths::join_with_separator('/', $TEST_INPUTS[$i]);
$expectedPath = $EXPECTED_OUTPUTS[$i];
$this->assertEquals($expectedPath, $actualPath);
}
}
}
我喜欢介绍的几种解决方案。但是那些确实将所有“ / +”替换为“ /”(正则表达式)的人却忘记了python的os.path.join()可以处理这种连接:
os.path.join('http://example.com/parent/path', 'subdir/file.html')