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

问题描述

我想创建我个人的 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"

它只删除第一次出现(文件夹 5/../):

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

$count 中的 preg_replace('~(?<![^/])(?!\.\.(?![^/]))[^/]+/\.\.(?:/|$)~',$count) 参数保持替换的次数,并且替换一直持续到找不到匹配项为止。

输出:

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

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

<?php
    
$strings = ["folders/folder1/folder5/../../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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...