Регулярное выражение для соответствия неограниченному числу вариантов

Я хочу смочь проанализировать пути к файлам как этот:

 /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

Прямо сейчас у меня есть a preg_match оператор, который может разделить две альтернативы:

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

Кто-то мог дать мне подсказку, как расширить это для принятия неограниченного количества альтернатив (по крайней мере два)? Только относительно регулярного выражения, остальные я могу иметь дело с.

Правило:

  • Список должен запуститься с a ( и согласитесь с a )

  • Должен быть тот | в списке (т.е. по крайней мере две альтернативы)

  • Любое другое возникновение (возникновение) ( или ) должны остаться нетронутыми.

Обновление: Я должен смочь также иметь дело с несколькими парами скобки, такими как:

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

извините я немедленно не сказал это.

Обновление 2: Если Вы надеетесь делать то, что я пытаюсь сделать в файловой системе, то обратите внимание, что шарик () уже выводит эту функциональность из поля. Нет никакой потребности реализовать пользовательское решение. См. ответ @Gordon ниже для деталей.

6
задан Pekka supports GoFundMonica 29 March 2010 в 09:11
поделиться

5 ответов

Решение без регулярных выражений :)

<?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
ответ дан 8 December 2019 в 16:00
поделиться

Возможно, я все еще не понимаю вопрос, но я предполагаю, что вы работаете через файловую систему, пока не нажмете один из файлов, и в этом случае вы можете сделать

$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.

2
ответ дан 8 December 2019 в 16:00
поделиться

Ответ дан, но это забавная загадка, и я просто не мог устоять.

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

может быть, это немного объясняет, почему мы так любим регулярные выражения;)

1
ответ дан 8 December 2019 в 16:00
поделиться

Не совсем то, что вы спрашиваете, но что плохого в том, чтобы просто взять то, что у вас есть, чтобы получить список (игнорируя | s), поместив его в переменную а затем взорвутся на | s? Это даст вам массив, сколько бы элементов ни было (включая 1, если нет | подарка).

4
ответ дан 8 December 2019 в 16:00
поделиться

Думаю, вы ищете:

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

В основном ставим разделитель '|' в повторяющийся узор.

Кроме того, согласно вашему третьему требованию, ваши слова должны состоять из «не трубы», а не «без скобок».

Кроме того, для этой задачи лучше использовать + вместо * . + означает «хотя бы один». * означает «ноль или больше».

5
ответ дан 8 December 2019 в 16:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: