Я пытаюсь думать о самом быстром способе реализации нечувствительной к регистру функции file_exists в PHP. Я предпочитаю перечислить файл в каталоге и выполнить сравнение strtolower () в strtolower (), пока не будет найдено совпадение?
Я использовал источник комментариев для создания этой функции. Возвращает полный файл пути, если он найден, 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; }
В Unix имена файлов чувствительны к регистру, поэтому вы не сможете выполнить проверку отсутствия регистра без учета содержимого каталога.
Ваш подход работает.
Кроме того, вы можете использовать glob
для получения списка всех файлов и каталогов в текущем рабочем каталоге в массиве, используйте array_map
для применения strtolower
к каждому элементу, а затем используйте in_array
чтобы проверить, существует ли ваш файл (после применения strtolower
) в массиве.
Я столкнулся с той же проблемой, когда мы перешли из 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(); }
Я настроил функцию чуть больше. предположим, что это лучше для использования
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; }
Этот вопрос несколько лет, но он связан с дубликатами, поэтому вот простой способ.
Возвращает false
если $filename
в любом случае не найден в $path
или фактическом имени файла первого файла, возвращаемого glob()
если он был найден в любом случае:
$result = current(preg_grep("/$filename$/i", glob("$path/*")));
Удалите current()
чтобы вернуть все соответствующие файлы. Это важно для файловых систем с IMAGE.jpg
image.JPG
так как IMAGE.jpg
и image.JPG
могут существовать.
Да, для чистой реализации PHP. В комментариях к функции file_exists
приведен file_exists
.
Другой вариант – запустить ваш скрипт в файловой системе, не учитывающей регистр.
Я улучшил функцию 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; }
Найдя эту страницу из быстрого google, я использовал решение 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
если вам не нужно это поведение, поэтому флаг казался излишним. Я также ожидаю, что если вы делаете что-либо помимо тривиального сценария, вы хотели бы превратить его в класс, чтобы получить больше контроля над кешированием списка каталогов, например, сбросить его, но это выходит за рамки того, что мне нужно, и оно должно если вам это нужно, попробуйте сделать это.
Мое настроенное решение, независимая от ОС, не зависящая от регистра 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]
выглядит скорее быстрым решением
//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); }
Просто наткнулся на это сегодня, но мне не понравился ни один из ответов здесь, поэтому я решил добавить свое решение (используя 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( file_exists( $filepath ) || false !== ( $filepath = file_iexists( $filepath ))){ //do something with $filepath }
Таким образом, он будет использовать встроенную в первую очередь, если это не удастся, он будет использовать нечувствительный и назначить надлежащую оболочку переменной $filepath
.
Ура!
Другие ответы могут быть очень ресурсоемкими в больших файловых системах (большое количество файлов для поиска). Возможно, было бы полезно создать временную таблицу всех имен файлов (если необходимо, полный путь). Затем выполните аналогичный поиск в этой таблице, чтобы получить все, что имеет место.
SELECT actual_file_name FROM TABLE_NAME WHERE actual_file_name LIKE 'filename_i_want'