PHP fnmatch()函数
fnmatch()函数用于根据shell通配符模式检查字符串是否匹配,常用于文件名匹配和字符串模式匹配。
语法
bool fnmatch ( string $pattern , string $string [, int $flags = 0 ] )
参数说明
| 参数 |
描述 |
pattern |
shell通配符模式,支持的特殊字符包括:* ? [ ] |
string |
要检查的字符串(通常是文件名) |
flags |
可选标志,可以是以下常量的组合:
FNM_NOESCAPE - 禁用反斜杠转义
FNM_PATHNAME - 字符串中的斜杠只匹配模式中的斜杠
FNM_PERIOD - 字符串中的前导点必须与模式中的点显式匹配
FNM_CASEFOLD - 不区分大小写匹配
|
通配符规则
| 通配符 |
描述 |
示例 |
匹配结果 |
* |
匹配任意数量的任意字符(包括零个字符) |
*.txt |
file.txt, document.txt, a.txt, .txt |
? |
匹配任意单个字符 |
file?.txt |
file1.txt, fileA.txt, file_.txt |
[abc] |
匹配方括号中的任意一个字符 |
file[123].txt |
file1.txt, file2.txt, file3.txt |
[a-z] |
匹配指定范围内的任意一个字符 |
file[a-c].txt |
filea.txt, fileb.txt, filec.txt |
[!abc] 或 [^abc] |
匹配不在方括号中的任意一个字符 |
file[!12].txt |
file3.txt, fileA.txt, filea.txt |
示例代码
示例1:基本文件名匹配
<?php
// 测试各种通配符模式
$patterns = [
'*.php' => ['index.php', 'test.php', 'file.txt', 'script.php.bak'],
'file?.txt' => ['file1.txt', 'fileA.txt', 'file.txt', 'file12.txt'],
'image[0-9].jpg' => ['image1.jpg', 'image2.jpg', 'image10.jpg', 'imageA.jpg'],
'test[!0-9].txt' => ['testA.txt', 'test1.txt', 'test.txt', 'test_.txt'],
'backup-*.zip' => ['backup-2023.zip', 'backup.zip', 'backup-2023-01.zip'],
];
foreach ($patterns as $pattern => $files) {
echo "模式: $pattern<br>";
foreach ($files as $file) {
$result = fnmatch($pattern, $file) ? '✓ 匹配' : '✗ 不匹配';
echo " $file: $result<br>";
}
echo "<br>";
}
?>
示例2:扫描目录并过滤文件
<?php
// 扫描目录并过滤特定类型的文件
function findFiles($directory, $pattern) {
$files = scandir($directory);
$result = [];
foreach ($files as $file) {
// 跳过当前目录和上级目录
if ($file === '.' || $file === '..') {
continue;
}
// 检查文件是否匹配模式
if (fnmatch($pattern, $file)) {
$result[] = $file;
}
}
return $result;
}
// 使用示例
$directory = '.';
// 查找所有PHP文件
$phpFiles = findFiles($directory, '*.php');
echo "PHP文件 (" . count($phpFiles) . "个):<br>";
echo implode('<br>', $phpFiles) . "<br><br>";
// 查找所有图片文件
$imagePatterns = ['*.jpg', '*.jpeg', '*.png', '*.gif'];
$allImages = [];
foreach ($imagePatterns as $pattern) {
$images = findFiles($directory, $pattern);
$allImages = array_merge($allImages, $images);
}
echo "图片文件 (" . count($allImages) . "个):<br>";
echo implode('<br>', $allImages);
?>
示例3:使用flags参数
<?php
// 测试不同的flags
$pattern = '*.TXT';
$files = ['file.txt', 'FILE.TXT', 'file.TXT', 'document.txt'];
echo "原始模式: $pattern<br><br>";
// 1. 默认匹配(区分大小写)
echo "默认(区分大小写):<br>";
foreach ($files as $file) {
$match = fnmatch($pattern, $file) ? '✓' : '✗';
echo "$file: $match<br>";
}
echo "<br>";
// 2. 使用FNM_CASEFOLD(不区分大小写)
echo "使用FNM_CASEFOLD(不区分大小写):<br>";
foreach ($files as $file) {
$match = fnmatch($pattern, $file, FNM_CASEFOLD) ? '✓' : '✗';
echo "$file: $match<br>";
}
echo "<br>";
// 3. 使用FNM_PATHNAME
echo "使用FNM_PATHNAME:<br>";
$pattern2 = '*/*.php';
$paths = [
'src/index.php',
'src/lib/test.php',
'index.php',
'test/.php'
];
foreach ($paths as $path) {
$match = fnmatch($pattern2, $path, FNM_PATHNAME) ? '✓' : '✗';
echo "$path: $match<br>";
}
?>
示例4:高级文件过滤类
<?php
class FilePatternMatcher {
private $patterns = [];
/**
* 添加匹配模式
* @param string $pattern 通配符模式
* @param int $flags 匹配标志
*/
public function addPattern($pattern, $flags = 0) {
$this->patterns[] = [
'pattern' => $pattern,
'flags' => $flags
];
return $this;
}
/**
* 检查文件是否匹配任何模式
*/
public function matches($filename) {
foreach ($this->patterns as $rule) {
if (fnmatch($rule['pattern'], $filename, $rule['flags'])) {
return true;
}
}
return false;
}
/**
* 过滤文件数组
*/
public function filterFiles($files) {
return array_filter($files, [$this, 'matches']);
}
/**
* 从目录中查找匹配的文件
*/
public function findInDirectory($directory, $recursive = false) {
$results = [];
$files = scandir($directory);
foreach ($files as $file) {
if ($file === '.' || $file === '..') {
continue;
}
$fullPath = $directory . DIRECTORY_SEPARATOR . $file;
// 检查当前文件
if ($this->matches($file)) {
$results[] = $fullPath;
}
// 递归搜索子目录
if ($recursive && is_dir($fullPath)) {
$subResults = $this->findInDirectory($fullPath, true);
$results = array_merge($results, $subResults);
}
}
return $results;
}
}
// 使用示例:创建图片文件过滤器
$imageMatcher = new FilePatternMatcher();
$imageMatcher
->addPattern('*.jpg', FNM_CASEFOLD)
->addPattern('*.jpeg', FNM_CASEFOLD)
->addPattern('*.png', FNM_CASEFOLD)
->addPattern('*.gif', FNM_CASEFOLD)
->addPattern('*.bmp', FNM_CASEFOLD);
// 查找当前目录下的所有图片
$images = $imageMatcher->findInDirectory('.');
echo "找到的图片文件 (" . count($images) . "个):<br>";
foreach ($images as $image) {
echo htmlspecialchars($image) . "<br>";
}
// 过滤现有文件数组
$files = ['test.jpg', 'document.pdf', 'image.PNG', 'backup.zip', 'photo.jpeg'];
$filtered = $imageMatcher->filterFiles($files);
echo "<br>过滤后的文件: " . implode(', ', $filtered);
?>
注意事项
重要提示:
- Windows系统:在Windows上,fnmatch()函数默认不区分大小写,但在Unix-like系统上区分大小写
- 转义字符:默认情况下,反斜杠(\)是转义字符。使用FNM_NOESCAPE标志可以禁用转义
- 性能考虑:对于大量文件的匹配,考虑使用preg_match()配合正则表达式,性能可能更好
- 模式复杂性:fnmatch()不支持完整的正则表达式语法,只支持shell通配符
- 字符编码:fnmatch()对多字节字符的支持可能有限,对于非ASCII文件名要谨慎使用
- 路径分隔符:使用FNM_PATHNAME标志时,斜杠(/)在模式中具有特殊意义,不会与*或?匹配
示例5:fnmatch与preg_match的对比
<?php
// 使用fnmatch进行shell模式匹配
$shellPattern = 'file*.txt';
$filename = 'file123.txt';
if (fnmatch($shellPattern, $filename)) {
echo "fnmatch: 匹配 ($shellPattern)<br>";
} else {
echo "fnmatch: 不匹配<br>";
}
// 使用preg_match进行正则匹配(等效模式)
$regexPattern = '/^file.*\.txt$/';
if (preg_match($regexPattern, $filename)) {
echo "preg_match: 匹配 ($regexPattern)<br>";
} else {
echo "preg_match: 不匹配<br>";
}
// 复杂的模式转换示例
function shellToRegex($pattern) {
// 将shell通配符模式转换为正则表达式
$regex = preg_quote($pattern, '/');
// 替换通配符
$regex = str_replace('\*', '.*', $regex);
$regex = str_replace('\?', '.', $regex);
// 处理字符组 [abc]
$regex = preg_replace_callback('/\\\\\[([^\[\]]*)\\\\\]/', function($matches) {
return '[' . str_replace('\\', '', $matches[1]) . ']';
}, $regex);
return '/^' . $regex . '$/';
}
// 测试转换函数
$shellPatterns = [
'*.php',
'file?.txt',
'image[0-9].jpg',
'test[!a-z].txt'
];
foreach ($shellPatterns as $shellPattern) {
$regex = shellToRegex($shellPattern);
echo "Shell模式: $shellPattern → 正则表达式: $regex<br>";
}
?>
相关函数
preg_match() - 执行正则表达式匹配
glob() - 查找与模式匹配的文件路径
scandir() - 列出指定路径中的文件和目录
pathinfo() - 返回文件路径的信息
basename() - 返回路径中的文件名部分
典型应用场景
- 文件类型过滤:根据扩展名过滤文件(如*.jpg, *.pdf等)
- 批量文件操作:对符合特定模式的文件执行批量操作
- 配置文件规则:实现类似.gitignore的文件排除规则
- 用户输入验证:验证用户输入的文件名是否符合特定格式
- 路由匹配:在简单的URL路由中使用通配符匹配