我正在尝试想出在PHP中实现不区分大小写的文件存在性检查函数的最快方法。我最好的选择是枚举目录中的文件并使用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;
}
fileExists
函数应该返回true
,如果文件存在 :) - vp_arthtrue
或 false
。 - hejdavfileExists('path')
,那么这段代码在Windows上无法正常工作。 - vee这个问题已经几年了,但它与多个问题被链接为重复,所以这里有一个简单的方法。
如果在任何情况下都没有找到$path
中的$filename
,则返回false
;如果在任何情况下找到了$filename
,则返回glob()
返回的第一个文件的实际文件名:
$result = current(preg_grep("/^".preg_quote($filename)."$/i", glob("$path/*")));
glob
下的所有文件i
标志在文件中查找$filename
current
返回数组中第一个文件名移除current()
来返回所有匹配的文件。这在区分大小写的文件系统上非常重要,因为IMAGE.jpg
和image.JPG
都可能存在。
$filename
变量用作preg_grep
模式之前,您应该对其进行转义,以避免由文件名中的某些字符((
、)
、-
等)引起的匹配失败。
安全语句应为:
$result = current(preg_grep('/'.preg_quote($filename).'$/i', glob("$path/*")));
- Michele DC$result = current(preg_grep("/\/".preg_quote($filename)."/i", glob("$path/*")));
- Marco Scarnatto当我们从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();
}
AbraCadaver的回答得到了+7的评分,但是他的回答是不正确的。我没有足够的声望在他的回答下发表评论,所以在此提供一个基于他的回答的正确解决方案:
Original Answer翻译成"最初的回答"
$result = count(preg_grep('/\/'.preg_quote($filename)."$/i", glob("$path/*")));
你的方法可行。
或者你可以使用glob
在数组中获取当前工作目录中所有文件和目录的列表,使用array_map
将strtolower
应用于每个元素,然后使用in_array
检查是否存在(在应用strtolower
之后)与你的文件名相同的元素。
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
变量分配适当的大小写。
通过快速谷歌搜索找到了这个页面,我使用了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));
}
使用glob模式[nN][aA][mM][eE]
搜索文件名似乎是更快的解决方案。
file_exists()
在不区分大小写的文件系统上对文件不区分大小写。 - danorton