2010-10-19 15 views
12

Sto cercando di pensare al modo più veloce per implementare una funzione file_exists senza maiuscole e minuscole in PHP. La mia migliore scommessa per enumerare il file nella directory e fare un confronto strtolower() con strtolower() fino a trovare una corrispondenza?PHP Case Insensitive Version of file_exists()

+1

la cosa è, file_exists IS senza distinzione tra maiuscole e minuscole – Dwza

+1

-1 - questo ha bisogno di chiarimenti. È per un file system sensibile al maiuscolo/minuscolo. In caso contrario, la domanda è senza senso, poiché 'file_esistenze()' di PHP non fa distinzione tra maiuscole e minuscole per i file su file system senza distinzione tra maiuscole e minuscole. – danorton

risposta

24

Ho utilizzato la sorgente dai commenti per creare questa funzione. Restituisce il file di percorso completo se trovato, FALSE se non lo è.

Non funziona senza distinzione tra maiuscole e minuscole nei nomi di directory nel nome file.

function fileExists($fileName, $caseSensitive = true) { 

    if(file_exists($fileName)) { 
     return $fileName; 
    } 
    if($caseSensitive) return false; 

    // Handle case insensitive requests    
    $directoryName = dirname($fileName); 
    $fileArray = glob($directoryName . '/*', GLOB_NOSORT); 
    $fileNameLowerCase = strtolower($fileName); 
    foreach($fileArray as $file) { 
     if(strtolower($file) == $fileNameLowerCase) { 
      return $file; 
     } 
    } 
    return false; 
} 
+3

Non sarebbe bello restituire SEMPRE un nome file completo? È strano trovare a volte un percorso booleano e talvolta utile quando viene trovata una corrispondenza. –

+4

cosa vorresti restituire se il file non esiste? O_o – Jonathan

+1

Penso che la funzione 'fileExists' dovrebbe restituire' true', se il file esiste :) –

3

In Unix i nomi dei file sono case sensitive, quindi non sarà possibile eseguire un controllo di esistenza senza distinzione tra maiuscole e minuscole senza elencare il contenuto della directory.

0

Per un'implementazione PHP pura, sì. C'è un esempio in the comments for the file_exists function.

L'altra opzione sarebbe quella di eseguire lo script su un file system senza distinzione tra maiuscole e minuscole.

+0

Grazie! Finito usando questo nella mia risposta. –

2

Il tuo approccio funziona.
In alternativa è possibile utilizzare glob per ottenere l'elenco di tutti i file e le directory nella presente directory di lavoro in un array, utilizzare array_map per applicare strtolower a ciascun elemento e quindi utilizzare in_array per verificare se il file (dopo l'applicazione strtolower) esiste nell'array .

1

Ho riscontrato lo stesso problema durante la migrazione da IIS ad Apache. Di seguito è il pezzo che ho montato. Restituisce il percorso corretto come stringa o falso.

function resolve_path($path) 
{ 
    $is_absolute_path = substr($path, 0, 1) == '/'; 
    $resolved_path = $is_absolute_path ? '/' : './'; 
    $path_parts = explode('/', strtolower($path)); 

    foreach ($path_parts as $part) 
    { 
     if (!empty($part)) 
     { 
      $files = scandir($resolved_path); 

      $match_found = FALSE; 

      foreach ($files as $file) 
      { 
       if (strtolower($file) == $part) 
       { 
        $match_found = TRUE; 

        $resolved_path .= $file . '/'; 
       } 
      } 

      if (!$match_found) 
      { 
       return FALSE; 
      } 
     } 
    } 

    if (!is_dir($resolved_path) && !is_file($resolved_path)) 
    { 
     $resolved_path = substr($resolved_path, 0, strlen($resolved_path) - 1); 
    } 

    $resolved_path = $is_absolute_path ? $resolved_path : substr($resolved_path, 2, strlen($resolved_path)); 

    return $resolved_path; 
} 

$relative_path = substr($_SERVER['REQUEST_URI'], 1, strlen($_SERVER['REQUEST_URI'])); 
$resolved_path = resolve_path($relative_path); 

if ($resolved_path) 
{ 
    header('Location: http://' . $_SERVER['SERVER_NAME'] . '/' . $resolved_path); 
    die(); 
} 
0

ho migliorato la funzione John Himmelman s' e venire con questo:
suppose that i have a catch system \iMVC\kernel\caching\fileCache

function resolve_path($path) 
{ 
    # check if string is valid 
    if(!strlen($path)) return FALSE; 
    # a primary check 
    if(file_exists($path)) return $path; 
    # create a cache signiture 
    $cache_sig = __METHOD__."@$path"; 
    # open the cache file 
    $fc = new \iMVC\kernel\caching\fileCache(__CLASS__); 
    # check cache file and validate it 
    if($fc->isCached($cache_sig) && file_exists($fc->retrieve($cache_sig))) 
    { 
     # it was a HIT! 
     return $fc->retrieve($cache_sig); 
    } 
    # if it is ab 
    $is_absolute_path = ($path[0] == DIRECTORY_SEPARATOR); 
    # depart the path 
    $path_parts = array_filter(explode(DIRECTORY_SEPARATOR, strtolower($path))); 
    # normalizing array's parts 
    $path_parts = count($path_parts)? array_chunk($path_parts, count($path_parts)) : array(); 
    $path_parts = count($path_parts[0])?$path_parts[0]:array(); 
    # UNIX fs style 
    $resolved_path = $is_absolute_path ? DIRECTORY_SEPARATOR : "."; 
    # WINNT fs style 
    if(string::Contains($path_parts[0], ":")) 
    { 
     $is_absolute_path = 1; 
     $resolved_path = $is_absolute_path ? "" : ".".DIRECTORY_SEPARATOR; 
    } 
    # do a BFS in subdirz 
    foreach ($path_parts as $part) 
    { 
     if (!empty($part)) 
     { 
      $target_path = $resolved_path.DIRECTORY_SEPARATOR.$part; 
      if(file_exists($target_path)) 
      { 
       $resolved_path = $target_path; 
       continue; 
      } 
      $files = scandir($resolved_path); 

      $match_found = FALSE; 

      foreach ($files as $file) 
      { 
       if (strtolower($file) == $part) 
       { 
        $match_found = TRUE; 
        $resolved_path = $resolved_path.DIRECTORY_SEPARATOR.$file; 
        break; 
       } 
      } 
      if (!$match_found) 
      { 
       return FALSE; 
      } 
     } 
    } 
    # cache the result 
    $fc->store($target_path, $resolved_path); 
    # retrun the resolved path 
    return $resolved_path; 
} 
1

ho sintonizzato la funzione un po' lil più. Immagino che questo meglio per l'uso

function fileExists($fileName, $fullpath = false, $caseInsensitive = false) 
{ 
    // Presets 
    $status   = false; 
    $directoryName = dirname($fileName); 
    $fileArray  = glob($directoryName . '/*', GLOB_NOSORT); 
    $i    = ($caseInsensitive) ? "i" : ""; 

    // Stringcheck 
    if (preg_match("/\\\|\//", $fileName)) // Check if \ is in the string 
    { 
     $array = preg_split("/\\\|\//", $fileName); 
     $fileName = $array[ count($array) -1 ]; 
    } 

    // Compare String 
    foreach ($fileArray AS $file) 
    { 
     if(preg_match("/{$fileName}/{$i}", $file)) 
     { 
      $output = "{$directoryName}/{$fileName}"; 
      $status = true; 
      break; 
     } 
    } 

    // Show full path 
    if($fullpath && $status) 
     $status = $output; 

    // Return the result [true/false/fullpath (only if result isn't false)] 
    return $status; 
} 
0

Dopo aver trovato questa pagina da un google rapido ho usato Kirk 's soluzione, tuttavia è lento se lo si chiama più volte sulla stessa directory o in una directory che ha molti file in . Ciò è dovuto ad esso loop su tutti i file ogni volta, così ho ottimizzato un po ':

function fileExists($fileName) { 
    static $dirList = []; 
    if(file_exists($fileName)) { 
     return true; 
    } 
    $directoryName = dirname($fileName); 
    if (!isset($dirList[$directoryName])) { 
     $fileArray = glob($directoryName . '/*', GLOB_NOSORT); 
     $dirListEntry = []; 
     foreach ($fileArray as $file) { 
      $dirListEntry[strtolower($file)] = true; 
     } 
     $dirList[$directoryName] = $dirListEntry; 
    } 
    return isset($dirList[$directoryName][strtolower($fileName)]); 
} 

ho lasciato cadere la bandiera per verificare la presenza tra maiuscole e minuscole, come presumo che ci basta usare file_exists se didn' Ho bisogno di questo comportamento, quindi la bandiera sembrava ridondante. Mi aspetto anche che se stai facendo qualcosa oltre a uno script banale, vorresti trasformarlo in una classe per avere più controllo sulla memorizzazione nella cache dell'elenco di directory, ad es. resettandolo, ma questo va oltre lo scopo di ciò di cui avevo bisogno e dovrebbe essere banale da fare se ne hai bisogno.

1

Questa domanda è vecchia di alcuni anni ma è collegata a duplicati, quindi ecco un metodo semplice.

Restituisce false se il $filename in ogni caso non si trova nel $path o il nome del file effettivo del primo file restituito da glob() se è stato trovato in ogni caso:

$result = current(preg_grep("/$filename$/i", glob("$path/*"))); 

Rimuovere il current() di restituire tutto file corrispondenti. Questo è importante su file system sensibili al maiuscolo/minuscolo come IMAGE.jpg e image.JPG possono esistere entrambi.

0

La mia soluzione sintonizzati, indipendente dal sistema operativo, case-insensitive realpath() alternativa, che copre tutto il percorso, chiamato realpathi():

/** 
* Case-insensitive realpath() 
* @param string $path 
* @return string|false 
*/ 
function realpathi($path) 
{ 
    $me = __METHOD__; 

    $path = rtrim(preg_replace('#[/\\\\]+#', DIRECTORY_SEPARATOR, $path), DIRECTORY_SEPARATOR); 
    $realPath = realpath($path); 
    if ($realPath !== false) { 
     return $realPath; 
    } 

    $dir = dirname($path); 
    if ($dir === $path) { 
     return false; 
    } 
    $dir = $me($dir); 
    if ($dir === false) { 
     return false; 
    } 

    $search = strtolower(basename($path)); 
    $pattern = ''; 
    for ($pos = 0; $pos < strlen($search); $pos++) { 
     $pattern .= sprintf('[%s%s]', $search[$pos], strtoupper($search[$pos])); 
    } 
    return current(glob($dir . DIRECTORY_SEPARATOR . $pattern)); 
} 

di ricerca il nome del file con il glob [nN][aA][mM][eE] motivo sembra essere la soluzione più veloce

-1

Le altre risposte potrebbero consumare molta risorse su file system di grandi dimensioni (un gran numero di file da cercare). Potrebbe essere utile creare una tabella temporanea di tutti i nomi di file (percorso completo se necessario). Quindi fai una ricerca di condizioni simili a quella tabella per ottenere qualunque sia il caso reale.

SELECT actual_file_name 
FROM TABLE_NAME 
WHERE actual_file_name LIKE 'filename_i_want' 
0
//will resolve & print the real filename 
$path = "CaseInsensitiveFiLENAME.eXt"; 
$dir = "nameOfDirectory"; 

if ($handle = opendir($dir)) { 
while (false !== ($entry = readdir($handle))) { 
    if (strtolower($path) == strtolower($entry)){ 
     echo $entry ; 
    }} 
    closedir($handle); 
} 
0

Proprio imbattuto in questo oggi, ma non come una delle risposte qui, così ho pensato che vorrei aggiungere la mia soluzione (utilizzando SPL e l'iteratore regex)

function _file_exists($pathname){ 
    try{ 
     $path = dirname($pathname); 
     $file = basename($pathname); 

     $Dir = new \FilesystemIterator($path, \FilesystemIterator::UNIX_PATHS); 
     $regX = new \RegexIterator($Dir, '/(.+\/'.preg_quote($file).')$/i', \RegexIterator::MATCH); 

     foreach ($regX as $p) return $p->getPathname(); 

    }catch (\UnexpectedValueException $e){ 
     //invalid path 
    } 
    return false; 
} 

il modo in cui sto usando è in questo modo:

$filepath = 'path/to/file.php'; 

if(false !== ($filepath = _file_exists($filepath))){ 
     //do something with $filepath 
} 

in questo modo si utilizzerà il costruita in un primo momento, se questo fallisce userà l'ins uno competitivo e assegnare l'involucro corretto alla variabile $filepath.

Problemi correlati