2010-03-28 13 views
6

voglio essere in grado di analizzare i percorsi dei file come questo:espressioni regolari per abbinare un numero illimitato di opzioni

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

in un array ordinato:

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

e poi produrre un elenco di alternative :

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

In questo momento, mi hai dichiarazioni preg_match che può dividere due alternative:

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

Qualcuno potrebbe darmi un puntatore come estendere questo per accettare un illimitato numero di alternative (almeno due)? Per quanto riguarda l'espressione regolare, il resto posso occuparmi.

La regola è:

  • L'elenco deve iniziare con un ( e chiudere con un )

  • Ci deve essere uno | nella lista (cioè almeno due alternative)

  • Qualsiasi altro evento (i) di ( o ) non deve essere toccato.

Aggiornamento: ho bisogno di essere in grado di affrontare anche con più coppie di parentesi come ad esempio:

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

dispiace non ho detto che subito.

Aggiornamento 2: Se stai cercando di fare quello che sto cercando di fare nel filesystem, quindi notare che glob() porta già questa funzionalità out of the box. Non è necessario implementare un controllo personalizzato. Vedi @ risposta di Gordon sotto per i dettagli.

+1

Perché l'inferno è tutti * * ossessionato ** con regex? Basta usare un diverso metodo maledetto. Abbinare tutto dopo una parentesi è ** non difficile ** senza regex. –

+0

@Coronatus mi mostra un elegante, leggero metodo non regex che può 1. gestire coppie multiple di parentesi e 2. ignorare qualsiasi coppia di parentesi che non contenga '|', e non sembra merda, e io ' ti attirò volentieri il mio cappello. –

+1

Fatto. Vedi la mia risposta. –

risposta

3

Soluzione non regex :)

<?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

Lavoro molto bello - Complimenti a te. * Ma * non può occuparsi di coppie di parentesi multiple come ho fatto * non * menzionato nella mia domanda - lo correggerò subito - ma * ha * nella mia sfida a voi. :) Penso che questo approccio sia difficile da estendere in modo che possa gestire coppie multiple di parentesi. O mi sbaglio? –

+0

Ok, sono convinto. Dividerò le coppie multiple di parentesi utilizzando una semplice espressione regolare, quindi eseguirò la funzione su di esse. Funziona troppo bene per non usare :) –

+0

Le coppie di parentesi multiple significano come '(html | php (4 | 5))'? Non sono sicuro di aver capito ma aggiornerò il codice se puoi confermare questo. Il codice attualmente corrisponde solo alla coppia dell'ultima staffa. –

4

Non è esattamente quello che stai chiedendo, ma cosa c'è di sbagliato con solo prendendo quello che hai per ottenere l'elenco (ignorando il | s), metterlo in una variabile e poi explode ing sulle | s? Questo ti darebbe una serie di molti elementi (incluso 1 se non ci fosse un | presente).

+0

Vero, buon punto. Provalo adesso. –

5

Penso che stai cercando:

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

In sostanza, mettere lo splitter '|' in uno schema ripetitivo.

Inoltre, le tue parole dovrebbero essere composte 'non pipe' invece di 'non parens', secondo il tuo terzo requisito.

Inoltre, preferire + a * per questo problema. + significa 'almeno uno'.* significa 'zero o più'.

+0

Cheers @CWF, questo è esattamente quello che ho chiesto. Ho finito i voti per oggi, altrimenti avrei fatto +1. Ne esaminerò un po 'domani, non sono ancora sicuro di come costruire le stringhe di variazione, potrei aver bisogno di un preg_match_callback - ci proverò. Comunque, grazie mille per lo schema ripetuto. –

2

Forse non sono ancora ricevendo la questione, ma la mia ipotesi è che si sta correndo attraverso il filesystem fino a colpire uno dei file, nel qual caso si potrebbe do

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

L'array risultante conterrà qualsiasi file corrispondente alle estensioni in $ percorso o nessuno. Se è necessario includere i file in base a un ordine di estensione specifico, è possibile impostare foreach sull'array con un elenco ordinato di estensioni, ad es.

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

Edit: e sì, è possibile avere più le parentesi graffe in glob.

+0

Era * così * facile. Grazie Gordon. Non avevo idea che Glob potesse fare cose del genere. Non riesco in buona coscienza a non accettare la risposta data, poiché stavo chiedendo specificamente come analizzare la stringa, ma inserirò una nota sulla tua risposta alla domanda. –

+0

Per ulteriori informazioni, maggiori informazioni su 'GLOB_BRACE', con esempi, qui: http://de.php.net/manual/en/function.glob.php#88250 –

+0

Avvertenza minima:' GLOB_BRACE' non è disponibile su alcuni sistemi non GNU, incluso Solaris (ma è supportato su Windows). Cercherò di scoprire quali sono esattamente http://stackoverflow.com/questions/2536924/glob-brace-portability –

1

La risposta è data, ma è un puzzle divertente e io non riuscivo a resistere

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

forse questo spiega un po 'il motivo per cui ci piace espressioni regolari che molto;)

+0

@stereofrog sweet !!! +1. –

+0

@stereofrog tuttavia, il '\ w' dovrebbe essere espanso in qualcosa come' \ w \ d' per corrispondere a qualsiasi nome di file (standard) concepibile. –

Problemi correlati