正则表达式从带有分隔符的字符串中提取另一个表达式

时间:2011-08-23 09:54:16

标签: php regex routes

这个问题有点奇怪,我花了一些时间来推动我对正则表达式的了解,以达到它的目的。然而,我却陷入了最后一点。问题如下:

我有一个字符串(表示我正在修改的路由系统中的url),可能包含一个匹配某个段的正则表达式。例如:

$pattern = "/some/path/to/</[a-z]+/>regex_var1/location";

这里要注意的重点是:

  • 正则表达式在字符串中用</ />分隔(除非由于遗留原因而导致世界末日,否则这不是特别可选的。我宁愿保留原样)。< / LI>
  • />(regex_var1)之后的文本位是此参数匹配的名称。我需要将其保留在表达式之外,以使其与系统的其余部分兼容,足以说在此上下文中可以忽略它。
  • 此网址格式符合/some/path/to/another/location

我想要实现的是将给定格式(如上所示)分割成段。这些段用于回溯树遍历以将请求URI与控制器匹配。目前不支持正则表达式,我的意图是允许这样做。在过去,每个细分都用/表示,但是我在所包含的正则表达式中需要/个字符。如果我在它的当前形式中使用它,则表达式分为两个段。例如

$pattern = "/some/</([a-z]+)(/optional)?/>regex2/location";
$segments = preg_split('/(?<!<)\/(?!>)/', $pattern);

产生4个部分

// print_r($segments)
Array
(
    [0] => 
    [1] => some
    [2] => </([a-z]+)(
    [3] => optional)?/>regex2
    [4] => location
)

当我实际上只想要3

// print_r($segments)
Array
(
    [0] => 
    [1] => some
    [2] => </([a-z]+)(/optional)?/>regex2
    [3] => location
)

我对将整个网址与正则表达式匹配不感兴趣,这会破坏整个练习的重点。这个问题可能在单独的情况下看来是没有根据的,但是关于为什么我在此特定实现之后的详细信息超出了问题的范围。

3 个答案:

答案 0 :(得分:2)

嗯,我看不到只用正则表达式来做这件事的简单方法。您可能首先解析出正则表达式(/<\/.*?\/>[^\/]*/),将它们存储在一个数组中并用简单但不碰撞的东西($1)替换它们,然后运行正则表达式并重新插入正则表达式。

答案 1 :(得分:0)

另一种方法:

$str = "/some/</([a-z]+)(/optional)?/>regex2/location";
$out_segments = array();
$in_regex = false;
foreach(preg_split('+/+', $str) as $segment) {
    if ($in_regex) {
        if (substr($segment, 0, 1) === '>') {
            $in_regex = false;
        }
        $out_segments[count($out_segments) - 1] .= "/$segment";
        continue;
    }
    if (!$in_regex && substr($segment, -1, 1) === '<') {
        $segment = substr($segment, 0, -1);
        if ($segment !== '') {
            $out_segments[] = $segment;
        }
        $in_regex = true;
        $segment = '<';
    }
    if ($segment !== '') {
        $out_segments[] = $segment;
    }
}
var_dump($out_segments);

编辑:错误的伪代码看起来更容易。不过,这个想法并不是那么糟糕。

答案 2 :(得分:0)

您可以先尝试将字符串拆分为其组件,然后再处理它:

$url = '/some/location/</([a-z]+)(/optional)?/>regex2/here/or/there';
$reg = '#(.*?)(</.*?/>.*?(?=/|$))(.*)?#';
if( preg_match($reg, $url, $matches) ) {
    $result = array_merge(
        preg_split( '#/#', $matches[1], 0, PREG_SPLIT_NO_EMPTY),
        array( $matches[2] ),
        preg_split( '#/#', $matches[3], 0, PREG_SPLIT_NO_EMPTY)
    );
    print_r( $result );    
}

Array
(
    [0] => some
    [1] => location
    [2] => </([a-z]+)(/optional)?/>regex2
    [3] => here
    [4] => or
    [5] => there
)

正则表达式应始终位于$matches[2]中,因此无论网址出现在何处,您都可以找到它。