2010-10-19 36 views
12

我想在PHP中實現不區分大小寫的file_exists函數的最快方法。我最好的辦法是枚舉目錄中的文件並執行strtolower()到strtolower()比較,直到找到匹配項爲止?PHP案件不敏感版本file_exists()

+1

事情是,file_exists不區分大小寫 – Dwza 2014-02-03 15:59:34

+1

-1 - 這需要澄清。這是否適用於區分大小寫的文件系統。如果不是這樣,問題就是廢話,因爲PHP的'file_exists()'對大小寫不敏感的文件系統上的文件不區分大小寫。 – danorton 2014-03-28 19:54:24

回答

24

我用評論的來源來創建這個功能。如果找到則返回完整路徑文件,否則返回FALSE。

對文件名中的目錄名稱不區分大小寫。

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

總是返回完整的文件名不是很好嗎?當找到匹配時,有時會得到一個布爾值,有時候會是一條有用的路徑,這有點奇怪。 – 2010-10-19 06:15:15

+4

如果文件不存在,你會返回什麼? O_o – Jonathan 2013-11-28 13:44:15

+1

我認爲'fileExists'函數應該返回'true',如果文件存在:) – 2014-10-29 14:47:27

3

在Unix中,文件名是區分大小寫的,因此您將無法執行不區分大小寫的存在檢查而不列出目錄的內容。

0

對於純粹的PHP實現,是的。在the comments for the file_exists function中有一個例子。

另一種選擇是在不區分大小寫的文件系統上運行腳本。

+0

謝謝!在我的回答中結束了使用。 – 2010-10-19 02:31:34

2

您的方法有效。
或者,您可以使用glob獲取陣列中當前工作目錄中的所有文件和目錄的列表,使用array_mapstrtolower應用於每個元素,然後使用in_array檢查您的文件(在應用strtolower後)是否存在於陣列中。

1

當我們從IIS遷移到apache時,遇到了同樣的問題。下面是我鞭打的一塊。它將正確的路徑作爲字符串返回或爲false。

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

我提高了John Himmelman的功能,並提出了這一點:
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

我調整功能更加律位。猜這更好的使用

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

已經發現此頁面的快速谷歌我以前Kirk的解決方案,但是它的速度慢,如果你把它放在同一個目錄多次,或在具有許多文件的目錄這是由於它每次遍歷所有的文件,所以我優化了一點:

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)]); 
} 

我放棄了標誌檢查不區分大小寫,因爲我認爲你只是使用file_exists如果你沒」 t需要這種行爲,所以國旗似乎是多餘的。我也希望如果你做的事情不是一個簡單的腳本,你想把它變成一個類,以更好地控制目錄列表緩存,例如,重置它,但這超出了我所需要的範圍,如果你需要它,這應該是微不足道的。

1

這個問題已經過了幾年了,但它與重複項相關,所以這裏是一個簡單的方法。

返回false如果在任何情況下$filename沒有在$path或​​3210返回的第一個文件的實際文件名中發現,如果它在任何情況下被發現:

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

取出current()全部歸還匹配文件。這對於區分大小寫的文件系統很重要,因爲IMAGE.jpgimage.JPG都可以存在。

0

我的調整方案,獨立於操作系統,case-insensitive realpath()替代,覆蓋整個路徑,命名爲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)); 
} 

與水珠[nN][aA][mM][eE]模式搜索文件名似乎是更快的解決方案

-1

其他答案可能在大型文件系統(需要搜索大量文件)上非常耗費資源。創建一個包含所有文件名的臨時表(如有必要,請使用完整路徑)可能很有用。然後做一個類似的條件搜索該表以獲取任何實際情況。

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

今天這個跨越剛跑,但不喜歡任何這裏的答案,所以我想我會加入我的解決方案(使用SPL和正則表達式迭代器)

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; 
} 

我使用它的方式是這樣的:

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

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

這樣它將使用內置的第一個,如果失敗它將使用ins並且將適當的外殼分配給$filepath變量。