PHPで大文字と小文字を区別しないfile_exists関数を実装する最速の方法を考えようとしています。ディレクトリ内のファイルを列挙し、一致するものが見つかるまで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;
}
この質問は数年前のものですが、重複としていくつかにリンクされているため、ここに簡単な方法があります。
_$filename
_が_$path
_に見つからない場合は、false
を返します。いずれにせよ、glob()
によって返される最初のファイルの実際のファイル名が見つかりません。
_$result = current(preg_grep("/".preg_quote($filename)."/i", glob("$path/*")));
_
glob
内のすべてのファイルを取得します$filename
_のGrepは、どのような場合でもi
は大文字と小文字を区別しませんcurrent
は、配列から最初のファイル名を返しますcurrent()
を削除して、一致するすべてのファイルを返します。 _IMAGE.jpg
_と_image.JPG
_の両方が存在する可能性があるため、これは大文字と小文字を区別するファイルシステムで重要です。
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;
}
今日これに出くわしましたが、ここでの回答が気に入らなかったので、ソリューションを追加すると思いました(SPLと正規表現イテレーターを使用)
function _file_exists( $pathname ){
if(file_exists($pathname)) return $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
変数に割り当てます。
私の調整したソリューション、OSに依存しない、 大文字と小文字を区別しない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]
パターンでファイル名を検索する方が速い解決策のようです
クイックグーグルからこのページを見つけたので、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
を使用すると想定しているため、大文字と小文字を区別しないかどうかを確認するためにフラグを削除しました。そのため、フラグは冗長に見えました。また、些細なスクリプト以外のことをしている場合は、これをクラスに変換して、ディレクトリリストのキャッシュをより細かく制御できるようにすることも期待しています。それをリセットしますが、それは私が必要としたものの範囲を超えており、あなたがそれを必要とするならばそれをするのは簡単なはずです。
//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);
}
純粋なPHP実装の場合、はい。 file_exists
関数のコメント に例があります。
もう1つのオプションは、大文字と小文字を区別しないファイルシステムでスクリプトを実行することです。
改善しました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;
}
+7評価のAbraCadaverの回答は正しくありません。コメントするのに十分な評判がないので、彼の回答に基づいた正しい解決策を次に示します。
$result = count(preg_grep('/\/'.preg_quote($filename)."$/i", glob("$path/*")));
AbraCadaverの答えは正しくありません。これは、ファイルfoo.jpg
に対してテストし、anytext_foo.jpg
のようなファイルが存在する場合にtrueを返すためです。