2024 PHP Conference Japan

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 的值可以是以下旗標的任意組合,並以 二進位 OR (|) 運算子 連接。

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(
'\*' => '.*',
'\?' => '.',
'\[\!' => '[^',
'\[' => '[',
'\]' => ']',
'\.' => '\.',
'\\' => '\\\\'
);

// Forward slash in string must be in pattern:
if ($flags & FNM_PATHNAME) {
$transforms['\*'] = '[^/]*';
}

// Back slash should not be escaped:
if ($flags & FNM_NOESCAPE) {
unset(
$transforms['\\']);
}

// Perform case insensitive match:
if ($flags & FNM_CASEFOLD) {
$modifiers .= 'i';
}

// Period at start must be the same as pattern:
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 總是匹配反斜線,我認為使用 preq_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 相容函式
我的應用程式需要在 Windows 上運作,因此需要用到 fnmatch 函式。我在這裡查看並測試了兩個版本。Jk 的版本對我有效,soywiz 的版本則無效(在 WinXPSP2,PHP 5.2.3 上)。
它們之間唯一的區別是使用 addcslashes (soywiz 版) 而不是 preg_quote (jk 版)。它們_應該_都能運作,但由於某種原因,soywiz 的版本對我來說無效。所以結果可能因人而異。
然而,要讓 JK 的 fnmatch() 函式與文件中的範例一起使用,您還必須使用 strtr 函式替換 $pattern 中的 [ 和 ]。
<?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 是文字檔";
else
print "$fn 不是文字檔";
To Top