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() - 返回路径中的文件名部分

典型应用场景

  1. 文件类型过滤:根据扩展名过滤文件(如*.jpg, *.pdf等)
  2. 批量文件操作:对符合特定模式的文件执行批量操作
  3. 配置文件规则:实现类似.gitignore的文件排除规则
  4. 用户输入验证:验证用户输入的文件名是否符合特定格式
  5. 路由匹配:在简单的URL路由中使用通配符匹配