PHP Conference Japan 2024

fnmatch

(PHP 4 >= 4.3.0, PHP 5, PHP 7, PHP 8)

fnmatch将文件名与模式匹配

描述

fnmatch(字符串 $pattern, 字符串 $filename, 整数 $flags = 0): 布尔值

fnmatch() 检查传递的 $filename 是否与给定的 shell 通配符 $pattern 匹配。

参数

pattern

要匹配的 $pattern。通常,$pattern 将包含通配符,如 '?''*'

$pattern 参数中使用的通配符
通配符 描述
? 问号匹配任何单个字符。例如,模式 "file?.txt" 将匹配 "file1.txt""fileA.txt",但不会匹配 "file10.txt"
* 星号匹配零个或多个字符。例如,模式 "foo*.xml" 将匹配 "foo.xml""foobar.xml"
[ ] 方括号用于创建 ASCII 码点范围或字符集。例如,模式 "index.php[45]" 将匹配 "index.php4""index.php5",但不会匹配 "index.phpt"。常见的范围是 [0-9][a-z][A-Z]。可以在同一时间使用多个集合和范围,例如 [0-9a-zABC]
! 感叹号用于否定方括号内的字符。例如,"[!A-Z]*.html" 将匹配 "demo.html",但不会匹配 "Demo.html"
\ 反斜杠用于转义特殊字符。例如,"Name\?" 将匹配 "Name?",但不会匹配 "Names"

filename

被测试的字符串。此函数特别适用于文件名,但也可以用于普通字符串。

普通用户可能习惯于 shell 模式,或者至少习惯于最简单的 '?''*' 通配符形式,因此使用 fnmatch() 而不是 preg_match() 作为前端搜索表达式输入对于非编程用户来说可能更方便。

flags

$flags 的值可以是以下标志的任意组合,使用 按位或 (|) 运算符 连接。

fnmatch() 的所有可能标志列表
标志 描述
FNM_NOESCAPE 禁用反斜杠转义。
FNM_PATHNAME 字符串中的斜杠仅匹配给定模式中的斜杠。
FNM_PERIOD 字符串开头的句点必须与给定模式中的句点完全匹配。
FNM_CASEFOLD 不区分大小写的匹配。GNU 扩展的一部分。

返回值

如果匹配则返回 true,否则返回 false

示例

示例 #1 将颜色名称与 shell 通配符模式匹配

<?php
if (fnmatch("*gr[ae]y", $color)) {
echo
"某种灰色...";
}
?>

注释

警告

目前,此函数在非 POSIX 兼容系统(Windows 除外)上不可用。

参见

添加注释

用户贡献的注释 7 条注释

me at rowanlewis dot com
14 年前
这是一个明确的解决方案,它支持负字符类和四个已记录的标志。

<?php

if (!function_exists('fnmatch')) {
define('FNM_PATHNAME', 1);
define('FNM_NOESCAPE', 2);
define('FNM_PERIOD', 4);
define('FNM_CASEFOLD', 16);

function
fnmatch($pattern, $string, $flags = 0) {
return
pcre_fnmatch($pattern, $string, $flags);
}
}

function
pcre_fnmatch($pattern, $string, $flags = 0) {
$modifiers = null;
$transforms = array(
'\*' => '.*',
'\?' => '.',
'\[\!' => '[^',
'\[' => '[',
'\]' => ']',
'\.' => '\.',
'\\' => '\\\\'
);

// 正斜杠在字符串中必须出现在模式中:
if ($flags & FNM_PATHNAME) {
$transforms['\*'] = '[^/]*';
}

// 反斜杠不应该被转义:
if ($flags & FNM_NOESCAPE) {
unset(
$transforms['\\']);
}

// 执行不区分大小写的匹配:
if ($flags & FNM_CASEFOLD) {
$modifiers .= 'i';
}

// 开头的句点必须与模式相同:
if ($flags & FNM_PERIOD) {
if (
strpos($string, '.') === 0 && strpos($pattern, '.') !== 0) return false;
}

$pattern = '#^'
. strtr(preg_quote($pattern, '#'), $transforms)
.
'$#'
. $modifiers;

return (boolean)
preg_match($pattern, $string);
}

?>

这可能需要进一步测试,但它似乎与原生 fnmatch 实现具有相同的功能。
Sinured
16 年前
补充我之前的一点说明:我关于 FNM_* 常量的陈述是错误的。它们在符合 POSIX 的系统上可用(换句话说,如果定义了 fnmatch())。
bernd dot ebert at gmx dot net
12 年前
pcre_fnmatch 函数中存在一个关于反斜杠的问题。如果未设置 FN_NOESCAPE,则这些反斜杠将被 preq_quote 以及 strtr 掩盖 -> 类似 "*a(*" 的内容最终将变成 "#^.*a\\(.*$#"。注意双反斜杠实际上并没有正确地掩盖 "("。

由于 preq_quote 始终匹配反斜杠,因此我认为根本无法使用 preg_quote。
Frederik Krautwald
17 年前
soywiz 的函数似乎仍然无法工作 -- 至少在 Windows 上使用 PHP 5.2.3 时无法工作 -- 但 jk 的函数可以。
jk at ricochetsolutions dot com
17 年前
soywiz 的函数对我来说似乎不起作用,但这个函数可以。

<?php
if(!function_exists('fnmatch')) {

function
fnmatch($pattern, $string) {
return
preg_match("#^".strtr(preg_quote($pattern, '#'), array('\*' => '.*', '\?' => '.'))."$#i", $string);
}
// 结束

} // 结束 if
?>
theboydanny at gmail dot com
17 年前
关于下面的 Windows 兼容函数
我需要 fnmatch 用于必须在 Windows 上运行的应用程序,查看了这里并测试了这两个函数。Jk 的函数对我来说可以工作,soywiz 的不行(在 WinXPSP2、PHP 5.2.3 上)。
它们之间唯一的区别是使用了 addcslashes (soywiz) 而不是 preg_quote (jk)。它们_应该_都能工作,但出于某种原因,soywiz 的函数对我来说不起作用。所以你的结果可能会有所不同。
但是,要使 JK 的 fnmatch() 与文档中的示例一起使用,您还必须将 $pattern 中的 [ 和 ] 进行 strtr 处理。
<?php
$pattern
= strtr(preg_quote($pattern, '#'), array('\*' => '.*', '\?' => '.', '\[' => '[', '\]' => ']'));
?>
感谢各位提供的函数。
phlipping at yahoo dot com
21 年前
您也可以尝试这个函数,我在找到 fnmatch 之前编写的

function WildToReg($str)
{
$s = "";
for ($i = 0; $i < strlen($str); $i++)
{
$c = $str{$i};
if ($c =='?')
$s .= '.'; // 任何字符
else if ($c == '*')
$s .= '.*'; // 0 个或多个任何字符
else if ($c == '[' || $c == ']')
$s .= $c; // [] 内的字符之一
else
$s .= '\\' . $c;
}
$s = '^' . $s . '$';

// 修剪冗余的 ^ 或 $
// 例如 ^.*\.txt$ 与 \.txt$ 完全匹配
if (substr($s,0,3) == "^.*")
$s = substr($s,3);
if (substr($s,-3,3) == ".*$")
$s = substr($s,0,-3);
return $s;
}

if (ereg(WildToReg("*.txt"), $fn))
print "$fn is a text file";
else
print "$fn is not a text file";
To Top