file_exists() 的 PHP 不区分大小写版本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3964793/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 11:35:03  来源:igfitidea点击:

PHP Case Insensitive Version of file_exists()

phpfilefile-io

提问by Kirk Ouimet

I'm trying to think of the fastest way to implement a case insensitive file_exists function in PHP. Is my best bet to enumerate the file in the directory and do a strtolower() to strtolower() comparison until a match is found?

我正在考虑在 PHP 中实现不区分大小写的 file_exists 函数的最快方法。我最好的办法是枚举目录中的文件并进行 strtolower() 与 strtolower() 的比较,直到找到匹配项?

回答by Kirk Ouimet

I used the source from the comments to create this function. Returns the full path file if found, FALSE if not.

我使用评论中的源代码来创建这个函数。如果找到则返回完整路径文件,否则返回 FALSE。

Does not work case-insensitively on directory names in the filename.

对文件名中的目录名不区分大小写不起作用。

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

回答by AbraCadaver

This question is a few years old but it is linked to several as duplicates, so here is a simple method.

这个问题已经有几年了,但它与几个重复项相关联,所以这里有一个简单的方法。

Returns falseif the $filenamein any case is not found in the $pathor the actual filename of the first file returned by glob()if it was found in any case:

false如果$filename在任何情况下都没有在$path第一个文件的文件名或实际文件名中找到,则返回,如果在任何情况下都找到了,则返回glob()

$result = current(preg_grep("/".preg_quote($filename)."/i", glob("$path/*")));
  • Get all files in the path glob
  • Grep for the $filenamein any case iis case-insensitive
  • currentreturns the first filename from the array
  • 获取路径中的所有文件 glob
  • $filename在任何情况i下的Grep都不区分大小写
  • current返回数组中的第一个文件名

Remove the current()to return all matching files. This is important on case-sensitive filesystems as IMAGE.jpgand image.JPGcan both exist.

删除current()以返回所有匹配的文件。这是作为区分大小写的文件系统很重要IMAGE.jpg,并image.JPG能同时存在。

回答by Adam Byrtek

In Unix file names are case sensitive, so you won't be able to do a case insensitive existence check without listing the contents of the directory.

在 Unix 中文件名区分大小写,因此如果不列出目录的内容,您将无法进行不区分大小写的存在检查。

回答by codaddict

Your approach works.
Alternatively you can use globto get the list of all files and directories in the present working directory in an array, use array_mapto apply strtolowerto each element and then use in_arrayto check if your file(after applying strtolower) exists in the array.

你的方法有效。
或者,您可以使用glob获取数组中当前工作目录中所有文件和目录的列表,用于array_map应用strtolower到每个元素,然后用于in_array检查您的文件(应用后strtolower)是否存在于数组中。

回答by John Himmelman

I ran into the same issue when we migrated from IIS to apache. Below is the piece I whipped up. It returns either the correct path as a string or false.

当我们从 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();
}

回答by Dwza

I tuned the function a lil bit more. guess this better for use

我稍微调整了这个功能。猜这更好用

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

回答by Nick Peirson

Having found this page from a quick google I used Kirk's solution, however it's slow if you call it multiple times on the same directory, or on a directory that has many files in. This is due to it looping over all the files each time, so I optimised it a little:

从我使用的快速谷歌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)]);
}

I dropped the flag to check for case insensitivity as I assume you'd just use file_existsif you didn't need this behaviour, so the flag seemed redundant. I also expect that if you're doing anything beyond a trivial script you'd want to turn this into a class to get more control over the directory list caching, e.g. resetting it, but that's beyond the scope of what I needed and it should be trivial to do if you need it.

我删除了标志以检查不区分大小写,因为我假设file_exists如果您不需要这种行为,您只会使用,因此该标志似乎是多余的。我还希望,如果你在做一些简单的脚本之外的事情,你会想把它变成一个类来更好地控制目录列表缓存,例如重置它,但这超出了我需要的范围,它应该如果你需要它是微不足道的。

回答by hejdav

My tuned solution, OS independent, case-insensitive realpath()alternative, covering whole path, named realpathi():

我调整的解决方案,独立于操作系统,不区分大小写的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));
}

search filename with glob [nN][aA][mM][eE]pattern seems to be the faster solution

使用 glob[nN][aA][mM][eE]模式搜索文件名似乎是更快的解决方案

回答by dariush

I have improved John Himmelman's function and come up with this:
suppose that i have a catch system \iMVC\kernel\caching\fileCache

我改进了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;
}

回答by deceze

For a pure PHP implementation, yes. There's an example in the comments for the file_existsfunction.

对于纯 PHP 实现,是的。函数的注释中file_exists有一个示例。

The other option would be to run your script on a case insensitive file system.

另一种选择是在不区分大小写的文件系统上运行您的脚本。