2010-10-19 4 views
12

Я пытаюсь думать о самом быстром способе реализации нечувствительной к регистру функции file_exists в PHP. Я предпочитаю перечислить файл в каталоге и выполнить сравнение strtolower() в strtolower(), пока не будет найдено совпадение?PHP-регистр Нечувствительная версия file_exists()

+1

вещь, файл_exists IS нечувствителен к регистру – Dwza

+1

-1 - для этого требуется уточнение. Это для файловой системы, чувствительной к регистру. Если нет, то вопрос вздор, поскольку PHP 'file_exists()' не учитывает регистр для файлов в нечувствительных к регистру файловых системах. – danorton

ответ

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

Не было бы здорово ВСЕГДА вернуть полное имя файла? Это странно, когда иногда встречается логический, а иногда и полезный путь. –

+4

Что бы вы вернули, если файл не существует? O_o – Jonathan

+1

Я думаю, что функция 'fileExists' должна возвращать' true', если файл существует :) –

3

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

0

Для чистой реализации PHP да. Пример: the comments for the file_exists function.

Другой вариант - запустить скрипт в файловой системе, не учитывающей регистр.

+0

Спасибо! Закончилось использование этого в моем ответе. –

2

Ваш подход работает.
В качестве альтернативы вы можете использовать glob, чтобы получить список всех файлов и каталогов в настоящем рабочем каталоге в массиве, используйте array_map для применения strtolower к каждому элементу, а затем используйте 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 «s и придумали это:
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 «s решение, однако это медленно, если вы называете его несколько раз на одной и той же директории, или директории, который имеет большое количество файлов в . Это связано с его циклом по всем файлам каждый раз, поэтому я оптимизировал его немного:

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 если вы Ждут» Это нужно, поэтому флаг казался излишним. Я также ожидаю, что если вы делаете что-либо помимо тривиального сценария, вы хотели бы превратить это в класс, чтобы получить больше контроля над кешированием списка каталогов, например. перезагружая его, но это выходит за рамки того, что мне нужно, и это должно быть тривиально, если вам это нужно.

1

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

Возвращает false если $filename в любом случае не будет найден в $path или фактическое имя файла первого файла, возвращаемый glob(), если оно было найдено в любом случае:

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

Снимите current() вернуть все совпадающие файлы. Это важно для файловых систем, чувствительных к регистру, поскольку могут существовать IMAGE.jpg и image.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)); 
} 

поиск файла с Glob [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 
} 

таким образом, он будет использовать встроенный в один первый, если это не будет использовать модули и присвоить надлежащую оболочку переменной $filepath.

Смежные вопросы