Я хочу иметь возможность анализировать пути файлов, подобные этому:
/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 () уже выводит эту функциональность из коробки. Нет необходимости реализовывать пользовательский solutiom. См. Ниже приведенный ниже ответ Гордона.
Решение без регулярных выражений 🙂
<?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)'));
не<?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)'));
Я думаю, вы ищете:
/ (([^ |] +) (| ([^ |] +)) +) /
В принципе, поставьте разделитель '|' в повторяющийся узор.
Кроме того, ваши слова должны быть составлены «не трубы», а не «не parens», с вашего третьего требования.
Кроме того, предпочитайте +
для *
для этой проблемы. +
означает «по крайней мере один». *
означает «ноль или больше».
Не совсем то, что вы просите, но что не так, просто принимая то, что вам нужно, чтобы получить список (игнорируя | s), помещая его в переменную, а затем explode
на | s? Это дало бы вам массив из многих предметов, которые были (в том числе 1, если не было).
Может быть, я все еще не понимаю вопроса, но, по моему предположению, вы работаете через файловую систему, пока не нажмете один из файлов, и в этом случае вы могли бы сделать это
$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.
Ответ дан, но это забавная головоломка, и я просто не мог сопротивляться
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)!'));
может быть, это немного объясняет, почему нам так нравятся регулярные выражения;)