我注意到在输入标题时出现了一些类似的问题,但它们似乎不是关于PHP的。那么有没有使用PHP函数解决此问题的方法呢?
需要具体说明。
$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
$relpath = getRelativePath($a,$b); //needed function,should return '../../root/b/b.php'
任何好的建议吗?谢谢。我注意到在输入标题时出现了一些类似的问题,但它们似乎不是关于PHP的。那么有没有使用PHP函数解决此问题的方法呢?
需要具体说明。
$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
$relpath = getRelativePath($a,$b); //needed function,should return '../../root/b/b.php'
任何好的建议吗?谢谢。试试这个:
function getRelativePath($from, $to)
{
// some compatibility fixes for Windows paths
$from = is_dir($from) ? rtrim($from, '\/') . '/' : $from;
$to = is_dir($to) ? rtrim($to, '\/') . '/' : $to;
$from = str_replace('\\', '/', $from);
$to = str_replace('\\', '/', $to);
$from = explode('/', $from);
$to = explode('/', $to);
$relPath = $to;
foreach($from as $depth => $dir) {
// find first non-matching dir
if($dir === $to[$depth]) {
// ignore this directory
array_shift($relPath);
} else {
// get number of remaining dirs to $from
$remaining = count($from) - $depth;
if($remaining > 1) {
// add traversals up to first matching dir
$padLength = (count($relPath) + $remaining - 1) * -1;
$relPath = array_pad($relPath, $padLength, '..');
break;
} else {
$relPath[0] = './' . $relPath[0];
}
}
}
return implode('/', $relPath);
}
这将会给出。$a="/home/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ./root/b/b.php
和
$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../root/b/b.php
并且
$a="/home/root/a/a.php";
$b="/home/apache/htdocs/b/en/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../apache/htdocs/b/en/b.php
和
$a="/home/apache/htdocs/b/en/b.php";
$b="/home/root/a/a.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../../../root/a/a.php
getRelativePath("/home/some/../ws.a", "/home/modules/../ws.b");
=> ../../modules/../ws.b
http://sandbox.onlinephpfunctions.com/code/2634034cb4b3688b319a97b5a45ea2442ac2d419 - Edwin Rodríguezrealpath
运行它们以使其成为绝对路径。 - Gordonrealpath
也无法解决问题。 - Edwin Rodríguez由于我们已经有了几个答案,我决定测试它们并进行基准测试。 我使用以下路径进行测试:
$from = "/var/www/sites/web/mainroot/webapp/folder/sub/subf/subfo/subfol/subfold/lastfolder/";
注意:如果它是文件夹,则必须为函数放置尾随斜杠才能正常工作! 所以__DIR__
将不起作用。 相反,请使用__FILE__
或__DIR__ . '/'
$to = "/var/www/sites/web/mainroot/webapp/folder/aaa/bbb/ccc/ddd";
结果:(小数分隔符为逗号,千分位分隔符为点)
因此,我建议您使用Gordon的实现!(标记为答案的那一个)
Youg的函数也不错,并且对于简单目录结构(例如“a / b /c.php”)执行效果更佳,而Gordon的函数则对具有许多子目录(例如在此基准测试中使用的那些目录)的复杂结构执行效果更佳。
注意:我在下面写入了使用$from
和$to
作为输入返回的结果,以便您可以验证其中2个正确,而另外2个错误:
../../../../../../aaa/bbb/ccc/ddd
--> 正确../../../../../../aaa/bbb/ccc/ddd
--> 正确../../../../../../bbb/ccc/ddd
--> 错误../../../../../aaa/bbb/ccc/ddd
--> 错误'/'
上以$a和$b为参数使用explode函数,然后反向循环$aParts,将它们与$bParts的相同索引进行比较,直到找到“公共分母”目录(记录沿途的循环次数)。然后创建一个空字符串,并将'../'
添加到其中$numLoops-1次,然后将$b减去公共分母目录添加到该字符串中。const DS = DIRECTORY_SEPARATOR; // for convenience
function getRelativePath($from, $to) {
$dir = explode(DS, is_file($from) ? dirname($from) : rtrim($from, DS));
$file = explode(DS, $to);
while ($dir && $file && ($dir[0] == $file[0])) {
array_shift($dir);
array_shift($file);
}
return str_repeat('..'.DS, count($dir)) . implode(DS, $file);
}
array_intersect
函数的解决方案,因为如果并行目录具有相同的名称,这些解决方案将会失效。 例如,getRelativePath('start / A / end /','start / B / end /'
将返回“../ end”,因为array_intersect
会找到所有相同的名称,在本例中有2个,但实际上只应该有1个。str_repeat
和dirname
使代码更加简洁。太棒了!谢谢。 - mpen /**
* Returns the target path as relative reference from the base path.
*
* Only the URIs path component (no schema, host etc.) is relevant and must be given, starting with a slash.
* Both paths must be absolute and not contain relative parts.
* Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
* Furthermore, they can be used to reduce the link size in documents.
*
* Example target paths, given a base path of "/a/b/c/d":
* - "/a/b/c/d" -> ""
* - "/a/b/c/" -> "./"
* - "/a/b/" -> "../"
* - "/a/b/c/other" -> "other"
* - "/a/x/y" -> "../../x/y"
*
* @param string $basePath The base path
* @param string $targetPath The target path
*
* @return string The relative target path
*/
function getRelativePath($basePath, $targetPath)
{
if ($basePath === $targetPath) {
return '';
}
$sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
$targetDirs = explode('/', isset($targetPath[0]) && '/' === $targetPath[0] ? substr($targetPath, 1) : $targetPath);
array_pop($sourceDirs);
$targetFile = array_pop($targetDirs);
foreach ($sourceDirs as $i => $dir) {
if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
unset($sourceDirs[$i], $targetDirs[$i]);
} else {
break;
}
}
$targetDirs[] = $targetFile;
$path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs);
// A reference to the same base directory or an empty subdirectory must be prefixed with "./".
// This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
// as the first segment of a relative-path reference, as it would be mistaken for a scheme name
// (see http://tools.ietf.org/html/rfc3986#section-4.2).
return '' === $path || '/' === $path[0]
|| false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
? "./$path" : $path;
}
function getRelativePath($from, $to)
{
$from = explode('/', $from);
$to = explode('/', $to);
foreach($from as $depth => $dir)
{
if(isset($to[$depth]))
{
if($dir === $to[$depth])
{
unset($to[$depth]);
unset($from[$depth]);
}
else
{
break;
}
}
}
//$rawresult = implode('/', $to);
for($i=0;$i<count($from)-1;$i++)
{
array_unshift($to,'..');
}
$result = implode('/', $to);
return $result;
}
常见情况的简单一句话:
str_replace(getcwd() . DIRECTORY_SEPARATOR, '', $filepath)
或者:
substr($filepath, strlen(getcwd())+1)
要检查路径是否为绝对路径,请尝试:
$filepath[0] == DIRECTORY_SEPARATOR
以下是我的解决方法。出于某种未知原因,对于这个问题最受欢迎的答案并没有按预期工作。
public function getRelativePath($absolutePathFrom, $absolutePathDestination)
{
$absolutePathFrom = is_dir($absolutePathFrom) ? rtrim($absolutePathFrom, "\/")."/" : $absolutePathFrom;
$absolutePathDestination = is_dir($absolutePathDestination) ? rtrim($absolutePathDestination, "\/")."/" : $absolutePathDestination;
$absolutePathFrom = explode("/", str_replace("\\", "/", $absolutePathFrom));
$absolutePathDestination = explode("/", str_replace("\\", "/", $absolutePathDestination));
$relativePath = "";
$path = array();
$_key = 0;
foreach($absolutePathFrom as $key => $value)
{
if (strtolower($value) != strtolower($absolutePathDestination[$key]))
{
$_key = $key + 1;
for ($i = $key; $i < count($absolutePathDestination); $i++)
{
$path[] = $absolutePathDestination[$i];
}
break;
}
}
for ($i = 0; $i <= (count($absolutePathFrom) - $_key - 1); $i++)
{
$relativePath .= "../";
}
return $relativePath.implode("/", $path);
}
$a = "C:\xampp\htdocs\projects\SMS\App\www\App\index.php"
并且$b = "C:\xampp\htdocs\projects\SMS\App/www/App/bin/bootstrap/css/bootstrap.min.css"
$c
将是$b
相对于$a
的路径,即$c = getRelativePath($a, $b) = "bin/bootstrap/css/bootstrap.min.css"
有些原因,戈登的方法对我不起作用... 这是我的解决方案
function getRelativePath($from, $to) {
$patha = explode('/', $from);
$pathb = explode('/', $to);
$start_point = count(array_intersect($patha,$pathb));
while($start_point--) {
array_shift($patha);
array_shift($pathb);
}
$output = "";
if(($back_count = count($patha))) {
while($back_count--) {
$output .= "../";
}
} else {
$output .= './';
}
return $output . implode('/', $pathb);
}
我使用相同的数组操作得出了相同的结果:
function getRelativePath($path, $from = __FILE__ )
{
$path = explode(DIRECTORY_SEPARATOR, $path);
$from = explode(DIRECTORY_SEPARATOR, dirname($from.'.'));
$common = array_intersect_assoc($path, $from);
$base = array('.');
if ( $pre_fill = count( array_diff_assoc($from, $common) ) ) {
$base = array_fill(0, $pre_fill, '..');
}
$path = array_merge( $base, array_diff_assoc($path, $common) );
return implode(DIRECTORY_SEPARATOR, $path);
}
第二个参数是相对路径的文件。它是可选的,因此您可以获取相对路径,而不管当前所在的网页。 为了与@Young或@Gordon的示例一起使用,因为您想要知道从$a到$b的相对路径,您将不得不使用
getRelativePath($b, $a);