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
PHP Case Insensitive Version of file_exists()
提问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 false
if the $filename
in any case is not found in the $path
or 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
$filename
in any casei
is case-insensitive current
returns 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.jpg
and image.JPG
can 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 glob
to get the list of all files and directories in the present working directory in an array, use array_map
to apply strtolower
to each element and then use in_array
to 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_exists
if 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_exists
function.
对于纯 PHP 实现,是的。函数的注释中file_exists
有一个示例。
The other option would be to run your script on a case insensitive file system.
另一种选择是在不区分大小写的文件系统上运行您的脚本。