使用正则表达式的自定义 realpath()

Custom realpath() using regex

我想创建我个人的 realpath() 函数,它使用正则表达式并且不希望该文件存在。

到目前为止我做了什么

function my_realpath (string $path): string {
    if ($path[0] != '/') {
        $path = __DIR__.'/../../'.$path;
    }
    
    $path = preg_replace("~/\./~", '', $path);
    $path = preg_replace("~\w+/\.\./~", '', $path); // removes ../ from path

    return $path;
}

哪里不对

问题是如果我有这个字符串:

"folders/folder1/folder5/../../folder2"

它只删除第一次出现的 (folder5/../):

"folders/folder1/../folder2"

问题

如何删除(使用正则表达式)所有文件夹后跟相同数量的“../”?

例子

"folders/folder1/folder5/../../folder2" -> "folders/folder2"

"folders/folder1/../../../folder2" -> "../folder2"

"folders/folder1/folder5/../folder2" -> "folders/folder1/folder2"

我们可以告诉正则表达式:“~(\w+){n}/(../){n}~”,n 是贪婪的,但在两组中都一样吗?

您可以使用像

这样的基于递归的模式
preg_replace('~(?<=/|^)(?!\.\.(?![^/]))[^/]+/(?R)?\.\.(?:/|$)~', '', $url)

regex demo详情:

  • (?<=/|^) - 紧靠左侧,必须有 / 或字符串开头(如果字符串作为单独的字符串使用,等于更有效的 (?<![^/])
  • (?!\.\.(?![^/])) - 紧靠右边,不应有 .. 后跟 / 或字符串结尾
  • [^/]+ - /
  • 以外的一个或多个字符
  • / - 一个 / 字符
  • (?R)? - 递归整个模式,可选
  • \.\.(?:/|$) - .. 后跟一个 / 字符或字符串结尾。

参见 PHP demo:

$strings = ["folders/folder1/folder5/../../folder2", "folders/folder1/../../../folder2", "folders/folder1/folder5/../folder2"];
foreach ($strings as $url) {
    echo preg_replace('~(?<=/|^)(?!\.\.(?![^/]))[^/\n]+/(?R)?\.\.(?:/|$)~', '', $url) . PHP_EOL;
}
// => folders/folder2, ../folder2, folders/folder1/folder2

或者,您可以使用

(?<![^/])(?!\.\.(?![^/]))[^/]+/\.\.(?:/|$)

regex demo详情:

  • (?<![^/]) - 紧靠左侧,必须有字符串开头或 / 字符
  • (?!\.\.(?![^/])) - 紧靠右边,不应有 .. 后跟 / 或字符串结尾
  • [^/]+ - /
  • 以外的一个或多个字符
  • /\.\. - /.. 子字符串后跟...
  • (?:/|$) - / 或字符串结尾。

参见 PHP demo:

$strings = ["folders/folder1/folder5/../../folder2", "folders/folder1/../../../folder2", "folders/folder1/folder5/../folder2"];
foreach ($strings as $url) {
    $count = 0;
    do {
        $url = preg_replace('~(?<![^/])(?!\.\.(?![^/]))[^/]+/\.\.(?:/|$)~', '', $url, -1, $count);
    } while ($count > 0);
    echo "$url" . PHP_EOL;
}

preg_replace('~(?<![^/])(?!\.\.(?![^/]))[^/]+/\.\.(?:/|$)~', '', $url, -1, $count)中的$count参数保留替换的次数,替换一直进行到找不到匹配为止。

输出:

folders/folder2
../folder2
folders/folder1/folder2

您也可以使用非正则表达式方法:

<?php
    
$strings = ["folders/folder1/folder5/../../folder2", "folders/folder1/../../../folder2", "folders/folder1/folder5/../folder2"];
    
function make_path($string) {
    $parts = explode("/", $string);
    $new_folder = [];
    for ($i=0; $i<count($parts); $i++) {
        if (($parts[$i] == "..") and count($new_folder) >= 1) {
            array_pop($new_folder);
        } else {
            $new_folder[] = $parts[$i];
        }
    }
    return implode("/", $new_folder);
}
    
$new_folders = array_map('make_path', $strings);
print_r($new_folders);
?>

这会产生

Array
(
    [0] => folders/folder2
    [1] => ../folder2
    [2] => folders/folder1/folder2
)

参见a demo on ideone.com