web-dev-qa-db-ja.com

PHP大文字と小文字を区別しないバージョンのfile_exists()

PHPで大文字と小文字を区別しないfile_exists関数を実装する最速の方法を考えようとしています。ディレクトリ内のファイルを列挙し、一致するものが見つかるまでstrtolower()とstrtolower()の比較を行うのが最善の策ですか?

18
Kirk Ouimet

コメントのソースを使用してこの関数を作成しました。見つかった場合はフルパスファイルを返し、見つからなかった場合は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;
}
25
Kirk Ouimet

この質問は数年前のものですが、重複としていくつかにリンクされているため、ここに簡単な方法があります。

_$filename_が_$path_に見つからない場合は、falseを返します。いずれにせよ、glob()によって返される最初のファイルの実際のファイル名が見つかりません。

_$result = current(preg_grep("/".preg_quote($filename)."/i", glob("$path/*")));
_
  • パスglob内のすべてのファイルを取得します
  • _$filename_のGrepは、どのような場合でもiは大文字と小文字を区別しません
  • currentは、配列から最初のファイル名を返します

current()を削除して、一致するすべてのファイルを返します。 _IMAGE.jpg_と_image.JPG_の両方が存在する可能性があるため、これは大文字と小文字を区別するファイルシステムで重要です。

9
AbraCadaver

Unixでは、ファイル名では大文字と小文字が区別されるため、ディレクトリの内容を一覧表示せずに大文字と小文字を区別しない存在チェックを行うことはできません。

2
Adam Byrtek

あなたのアプローチはうまくいきます。
または、globを使用して、配列内の現在の作業ディレクトリ内のすべてのファイルとディレクトリのリストを取得し、array_mapを使用して各要素にstrtolowerを適用することもできます。次に、in_arrayを使用して、ファイル(strtolowerを適用した後)が配列に存在するかどうかを確認します。

2
codaddict

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();
}
1
John Himmelman

関数をもう少し調整しました。これを使用する方が良いと思います

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

今日これに出くわしましたが、ここでの回答が気に入らなかったので、ソリューションを追加すると思いました(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変数に割り当てます。

0
ArtisticPhoenix

私の調整したソリューション、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]パターンでファイル名を検索する方が速い解決策のようです

0
hejdav

クイックグーグルからこのページを見つけたので、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を使用すると想定しているため、大文字と小文字を区別しないかどうかを確認するためにフラグを削除しました。そのため、フラグは冗長に見えました。また、些細なスクリプト以外のことをしている場合は、これをクラスに変換して、ディレクトリリストのキャッシュをより細かく制御できるようにすることも期待しています。それをリセットしますが、それは私が必要としたものの範囲を超えており、あなたがそれを必要とするならばそれをするのは簡単なはずです。

0
Nick Peirson
//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
user1483887

純粋なPHP実装の場合、はい。 file_exists関数のコメント に例があります。

もう1つのオプションは、大文字と小文字を区別しないファイルシステムでスクリプトを実行することです。

0
deceze

改善しました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;
}
0
dariush

+7評価のAbraCadaverの回答は正しくありません。コメントするのに十分な評判がないので、彼の回答に基づいた正しい解決策を次に示します。

$result = count(preg_grep('/\/'.preg_quote($filename)."$/i", glob("$path/*")));

AbraCadaverの答えは正しくありません。これは、ファイルfoo.jpgに対してテストし、anytext_foo.jpgのようなファイルが存在する場合にtrueを返すためです。

0
fcunited