正则表达式匹配无限数量的选项

时间:2010-03-28 20:37:27

标签: php regex preg-match

我希望能够像这样解析文件路径:

 /var/www/index.(htm|html|php|shtml)

进入有序数组:

 array("htm", "html", "php", "shtml")

然后生成一个备选列表:

/var/www/index.htm
/var/www/index.html
/var/www/index.php
/var/www/index.shtml

现在,我有一个preg_match语句可以拆分两个替代方案:

 preg_match_all ("/\(([^)]*)\|([^)]*)\)/", $path_resource, $matches);

有人可以给我一个指针如何扩展它以接受无限数量的替代品(至少两个)吗?关于正则表达式,其余的我可以处理。

规则是:

  • 该列表需要以(开头,并以)

  • 结束
  • 列表中必须有一个|(即至少有两个备选方案)

  • ()的任何其他事件都不会受到影响。

更新:我需要能够处理多个括号对,例如:

 /var/(www|www2)/index.(htm|html|php|shtml)
抱歉,我没有马上说出来。

  

更新2:如果您正在寻找我正在尝试在文件系统中执行的操作,请注意glob()已经开箱即用。无需实施自定义解决方案。有关详细信息,请参阅@ Gordon的答案。

5 个答案:

答案 0 :(得分:5)

我认为你在寻找:

/(([^ |] +)(|([^ |] +))+)/

基本上,把拆分器'|'重复模式。

另外,根据你的第三个要求,你的话应该是'not pipes'而不是'not parens'。

此外,对于此问题,请更喜欢+*+表示'至少一个'。 *表示“零或更多”。

答案 1 :(得分:4)

不完全是你在问什么,但是如果只拿你的东西来获取列表(忽略| s),将它放入变量然后explode在| s上会出现什么问题?这会给你一个包含许多项目的数组(如果没有|则包括1个)。

答案 2 :(得分:3)

非正则表达式解决方案:)

<?php

$test = '/var/www/index.(htm|html|php|shtml)';

/**
 *
 * @param string $str "/var/www/index.(htm|html|php|shtml)"
 * @return array "/var/www/index.htm", "/var/www/index.php", etc
 */
function expand_bracket_pair($str)
{
    // Only get the very last "(" and ignore all others.
    $bracketStartPos = strrpos($str, '(');
    $bracketEndPos = strrpos($str, ')');

    // Split on ",".
    $exts = substr($str, $bracketStartPos, $bracketEndPos - $bracketStartPos);
    $exts = trim($exts, '()|');
    $exts = explode('|', $exts);

    // List all possible file names.
    $names = array();

    $prefix = substr($str, 0, $bracketStartPos);
    $affix = substr($str, $bracketEndPos + 1);
    foreach ($exts as $ext)
    {
        $names[] = "{$prefix}{$ext}{$affix}";
    }

    return $names;
}

function expand_filenames($input)
{
    $nbBrackets = substr_count($input, '(');

    // Start with the last pair.
    $sets = expand_bracket_pair($input);

    // Now work backwards and recurse for each generated filename set.
    for ($i = 0; $i < $nbBrackets; $i++)
    {
        foreach ($sets as $k => $set)
        {
            $sets = array_merge(
                $sets,
                expand_bracket_pair($set)
            );
        }
    }

    // Clean up.
    foreach ($sets as $k => $set)
    {
        if (false !== strpos($set, '('))
        {
            unset($sets[$k]);
        }
    }
    $sets = array_unique($sets);
    sort($sets);

    return $sets;
}

var_dump(expand_filenames('/(a|b)/var/(www|www2)/index.(htm|html|php|shtml)'));

答案 3 :(得分:2)

也许我还没有得到这个问题,但我的假设是你正在运行文件系统,直到你点击其中一个文件,在这种情况下你可以做到

$files = glob("$path/index.{htm,html,php,shtml}", GLOB_BRACE);

生成的数组将包含与$ path或none中的扩展名匹配的任何文件。如果您需要按特定的扩展订单包含文件,则可以在数组上foreach使用有序的扩展名列表,例如

foreach(array('htm','html','php','shtml') as $ext) {
    foreach($files as $file) {
        if(pathinfo($file, PATHINFO_EXTENSION) === $ext) {
            // do something
        }
    }
}

编辑:是的,您可以在glob中使用多个大括号。

答案 4 :(得分:1)

给出了答案,但这是一个有趣的谜题,我无法抗拒

function expand_filenames2($str) {
    $r = array($str);
    $n = 0;
    while(preg_match('~(.*?) \( ( \w+ \| [\w|]+ ) \) (.*) ~x', $r[$n++], $m)) {
        foreach(explode('|', $m[2]) as $e)
            $r[] = $m[1] . $e . $m[3];
    }
    return array_slice($r, $n - 1);
}  



print_r(expand_filenames2('/(a|b)/var/(ignore)/(www|www2)/index.(htm|html|php|shtml)!'));

也许这解释了为什么我们喜欢regexps那么多;)