文件_exists()的PHP不区分大小写版本

文件_exists()的PHP不区分大小写版本,php,file,file-io,Php,File,File Io,我正试图想出在PHP中实现不区分大小写的file_exists函数的最快方法。枚举目录中的文件并进行strtolower()到strtolower()的比较,直到找到匹配项,这是我的最佳选择吗?在Unix中,文件名区分大小写,因此在不列出目录内容的情况下,无法执行不区分大小写的存在性检查。对于纯PHP实现,是的。这里有一个例子 另一个选项是在不区分大小写的文件系统上运行脚本。您的方法有效。或者,您可以使用glob获取数组中当前工作目录中所有文件和目录的列表,使用array\u map将strto

我正试图想出在PHP中实现不区分大小写的file_exists函数的最快方法。枚举目录中的文件并进行strtolower()到strtolower()的比较,直到找到匹配项,这是我的最佳选择吗?

在Unix中,文件名区分大小写,因此在不列出目录内容的情况下,无法执行不区分大小写的存在性检查。

对于纯PHP实现,是的。这里有一个例子


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

您的方法有效。
或者,您可以使用
glob
获取数组中当前工作目录中所有文件和目录的列表,使用
array\u map
strtolower
应用于每个元素,然后使用
in\u array
检查您的文件(应用
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;
}

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

我改进了John Himmelman的函数,并得出以下结论:
假设我有一个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;
}

我稍微调整了一下函数。我猜这个更好用

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

在快速的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\u exists
,因此该标志似乎是多余的。我还希望,如果你在做一些琐碎的脚本之外的事情,你会想把它转换成一个类,以获得对目录列表缓存的更多控制,例如重置它,但这超出了我需要的范围,如果你需要的话,这应该是琐碎的事情。

这个问题已经有几年了,但它作为副本链接到了几个目录列表缓存,这里有一个简单的方法

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

$result = current(preg_grep("/".preg_quote($filename)."/i", glob("$path/*")));
  • 获取路径
    glob中的所有文件
  • 在任何情况下,
    i
    $filename
    的Grep都不区分大小写
  • current
    返回数组中的第一个文件名

删除
current()
以返回所有匹配的文件。这对于区分大小写的文件系统非常重要,如
IMAGE.jpg
IMAGE.jpg
都可以存在。

我的优化解决方案,独立于操作系统,可选,覆盖整个路径,名为
realpathi()

/**
*不区分大小写的realpath()
*@param string$path
*@return string | false
*/
函数realpathi($path)
{
$me=\uuuuu方法\uuuuu;
$path=rtrim(preg\u replace('\\[/\\\\]+\\\\'、目录分隔符、$path)、目录分隔符);
$realPath=realPath($path);
如果($realPath!==false){
返回$realPath;
}
$dir=dirname($path);
如果($dir===$path){
返回false;
}
$dir=$me$dir;
如果($dir==false){
返回false;
}
$search=strtolower(basename($path));
$pattern='';
对于($pos=0;$pos

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

对于大型文件系统(需要搜索大量文件),其他答案可能会占用大量资源。创建一个包含所有文件名的临时表(如有必要,完整路径)可能会很有用。然后对该表执行类似条件搜索,以获得实际情况

选择实际的文件名
从表\u名称
其中实际的文件名,如“filename\u i\u want”

今天刚刚遇到这个问题,但不喜欢这里的任何答案,所以我想我应该添加我的解决方案(使用SPL和regex迭代器)

我使用它的方式是这样的:

 $filepath = 'path/to/file.php';

 if( false !== ( $filepath = _file_exists( $filepath ))){
      //do something with $filepath
 }

这样,它将首先使用内置的一个,如果失败,它将使用不敏感的一个,并为
$filepath
变量指定适当的大小写。

Abracadver的+7评级回答不正确,我没有足够的声誉在下面发表评论,因此根据他的回答,这里是正确的解决方案:

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

Abracadver的答案是不正确的,因为如果您对文件
foo.jpg
进行测试,并且像
anytext\u foo.jpg
这样的文件存在,它会返回true。

谢谢!在我的回答中使用了这个。总是返回完整的文件名不是很好吗?当找到匹配项时,有时会得到一个布尔值,有时会得到一个有用的路径,这有点奇怪。如果文件不存在,您会返回什么?O我认为
fileExists
函数应该返回
true
,如果文件存在:)另一方面,我们应该为其他函数提供不区分大小写的变体,如
fopen
file\u get\u contents
。所以其他函数名(如as
searchFile($fn,$ci)
)在这里会更好。@Jonathan抛出异常将是最佳实践;-)然而正如@vp_arth所说,对于函数名,我希望
true
false
作为返回值。-1-这需要澄清。这适用于区分大小写的文件系统。如果不是,这个问题就是胡说八道,因为PHP的
file_exists()
对于不区分大小写的文件系统上的文件是不区分大小写的
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
 }
$result = count(preg_grep('/\/'.preg_quote($filename)."$/i", glob("$path/*")));