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

Regular Expression to match unlimited number of options

提问人:Pekka 提问时间:3/29/2010 最后编辑:Pekka 更新时间:3/29/2010 访问量:912

问:

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

 /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);

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

规则是:

  • 列表需要以 a 开头,以()

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

  • 任何其他事件或将保持不变。()

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

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

对不起,我没有马上说。

更新2:如果你想在文件系统中做我想做的事情,那么请注意 glob() 已经带来了这个功能。无需实现自定义解决方案。有关详细信息,请参阅下面的 @Gordon 的回答。

php 正则表达式 预匹配

评论

1赞 Amy B 3/29/2010
为什么每个人都痴迷于正则表达式?只是使用不同的该死的方法。在没有正则表达式的情况下,匹配括号后的所有内容并不困难
0赞 Pekka 3/29/2010
@Coronatus向我展示一个优雅、轻量级的非正则表达式方法,它可以 1.处理多个括号对和 2.忽略任何不包含且看起来不像狗屎的括号对,我很乐意向你致敬。|
0赞 Gordon 3/29/2010
我不明白这个问题。这是干什么用的?
0赞 Pekka 3/29/2010
@Gordon这是为了指定具有回退的文件资源的路径:加载具有第一个扩展名的文件,或者如果不存在,则加载第二个扩展名,或者如果不存在,则加载第三个扩展名。

答:

4赞 Blair McMillan 3/29/2010 #1

不完全是你要问的,但是只拿你所要得到的列表(忽略|s),把它放到一个变量中,然后在|s上爆炸有什么问题?这将为您提供一个包含任意数量的项目的数组(如果没有 | 存在,则包括 1 个)。

5赞 CWF 3/29/2010 #2

我想你正在寻找:

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

基本上,将拆分器“|”放入重复模式中。

此外,根据你的第三个要求,你的话应该由“不是管道”而不是“不是parens”组成。

另外,更喜欢这个问题。 表示“至少一个”。 表示“零或更多”。+*+*

评论

0赞 Pekka 3/29/2010
干杯@CWF,这正是我所要求的。我今天的选票已经用完了,否则我会+1。我明天会再研究一下,我还不确定如何构建变体字符串,我可能需要一个preg_match_callback - 会尝试。无论如何,非常感谢您的重复模式。
3赞 Amy B 3/29/2010 #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)'));

评论

0赞 Pekka 3/29/2010
非常好的工作 - 向你致敬。但它不能处理多个括号对,因为我在问题中没有提到 - 我会立即纠正 - 但在我向你的挑战中提到了。:)我认为这种方法很难扩展,因此它可以处理多个括号对。还是我弄错了?
0赞 Pekka 3/29/2010
好吧,我深信不疑。我将使用简单的正则表达式拆分多个括号对,然后在它们上运行函数。这太好了,不能不使用:)
0赞 Amy B 3/29/2010
多个括号对是否意味着喜欢?我不确定我是否理解,但如果你能确认这一点,我会更新代码。该代码目前仅与最后一个括号对匹配。(html|php(4|5))
0赞 Pekka 3/29/2010
看我的更新,那里有一个例子。如果你愿意,请随时尝试是否也能实现 - 这对我有用,但我已经可以使用了。
0赞 Amy B 3/29/2010
修复了无限对括号。
2赞 Gordon 3/29/2010 #4

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

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

生成的数组将包含与您的扩展名匹配的任何文件($path或无)。如果需要按特定的扩展名顺序包含文件,则可以使用有序列表的扩展名来覆盖数组,例如foreach

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

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

评论

0赞 Pekka 3/29/2010
就这么简单。谢谢戈登。我不知道 Glob 可以做这样的事情。我不能凭良心不接受给出的答案,因为我专门询问如何解析字符串,但我会在问题中注明您的答案。
0赞 Pekka 3/29/2010
为了将来参考,请在此处提供更多信息和示例:de.php.net/manual/en/function.glob.php#88250GLOB_BRACE
0赞 Pekka 3/29/2010
小警告:在某些非 GNU 系统上不可用,包括 Solaris(但在 Windows 上受支持)。我会试着找出哪些 stackoverflow.com/questions/2536924/glob-brace-portabilityGLOB_BRACE
1赞 user187291 3/29/2010 #5

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

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)!'));

也许这在一定程度上解释了为什么我们;)那么喜欢正则表达式

评论

0赞 Pekka 3/29/2010
但是,@stereofrog,需要将其扩展为与任何可以想象的(标准)文件名相匹配的内容。\w\w\d.