fnmatch

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

fnmatchファイル名がパターンにマッチするか調べる

説明

fnmatch(string $pattern, string $filename, int $flags = 0): bool

fnmatch()filename で指定された文字列が pattern で指定されたシェルワイルドカードにマッチするかどうかチェックします。

パラメータ

pattern

マッチさせるパターン。 通常、pattern には '?''*' のようなワイルドカードを含めます。

pattern で使えるワイルドカード
ワイルドカード 説明
? クエスチョンマークは、1文字にマッチします。 たとえば、 "file?.txt""file1.txt""fileA.txt" にはマッチしますが、 "file10.txt" にはマッチしません。
* アスタリスクは、0文字以上の文字にマッチします。 たとえば、 "foo*.xml""foo.xml""foobar.xml" にマッチします。
[ ] 角括弧は、アスキー文字のコードポイントや、 文字の集合を表すのに使います。 たとえば、"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

調べたい文字列。この機能は特にファイル名のマッチに便利ですが、 通常の文字列に関しても使用できます。

一般的なユーザーにとって、シェルパターンやあるいは少なくとも '?''*'によるワイルドカードのほうが 慣れていると思われます。そのため、 preg_match() の代わりに fnmatch() をフロントエンドの検索表現として使うことは、 プログラマではないユーザーにとってより便利でしょう。

flags

flags の値は、以下のフラグを 論理 OR (|) 演算子 で連結した任意の組み合わせです。

fnmatch() で使えるフラグ
フラグ 説明
FNM_NOESCAPE バックスラッシュのエスケープを無効にする。
FNM_PATHNAME 文字列内のスラッシュは、指定したパターン内のスラッシュにのみマッチする。
FNM_PERIOD 文字列の先頭のピリオドは、指定したパターン内のピリオドにマッチしなければならない。
FNM_CASEFOLD 大文字小文字を区別しないマッチ。GNU 拡張の一部。

戻り値

マッチした場合に true、それ以外の場合に false を返します。

例1 シェルのワイルドカードパターンによる色の名前のチェック

<?php
if (fnmatch("*gr[ae]y", $color)) {
echo
"some form of gray ...";
}
?>

注意

警告

今のところ、この機能は POSIX に準拠していないシステムで利用できません (ただし、Windows では利用できます)。

参考

  • glob() - パターンにマッチするパス名を探す
  • preg_match() - 正規表現によるマッチングを行う
  • sscanf() - フォーマット文字列に基づき入力を処理する
  • printf() - フォーマット済みの文字列を出力する
  • sprintf() - フォーマットされた文字列を返す

add a note

User Contributed Notes 8 notes

up
14
me at rowanlewis dot com
14 years ago
Here's a definitive solution, which supports negative character classes and the four documented flags.

<?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);
    }
    
?>

This probably needs further testing, but it seems to function identically to the native fnmatch implementation.
up
1
Sinured
17 years ago
An addition to my previous note: My statement regarding the FNM_* constants was wrong. They are available on POSIX-compliant systems (in other words, if fnmatch() is defined).
up
0
daniel at elementor dot com
3 months ago
The default value of flags is 0, if not specified.
up
0
bernd dot ebert at gmx dot net
13 years ago
There is a problem within the  pcre_fnmatch-Function concerning backslashes. Those will be masked by preq_quote and ADDITONALLY by the strtr if FN_NOESCAPE is not set -> something like "*a(*" will finally result in "#^.*a\\(.*$#". Note the double backslash which effectively does NOT mask the "(" correctly. Since preq_quote always matches a backslash I don't think that this'll work with using preg_quote at all.
up
0
Frederik Krautwald
18 years ago
soywiz's function still doesn't seem to work -- at least not with PHP 5.2.3 on Windows -- but jk's does.
up
-1
jk at ricochetsolutions dot com
18 years ago
soywiz's function didnt seem to work for me, but this did.<?phpif(!function_exists('fnmatch')) {    function fnmatch($pattern, $string) {        return preg_match("#^".strtr(preg_quote($pattern, '#'), array('\*' => '.*', '\?' => '.'))."$#i", $string);    } // end} // end if?>
up
-2
theboydanny at gmail dot com
17 years ago
About the windows compat functions below:I needed fnmatch for a application that had to work on Windows, took a look here and tested both. Jk's works for me, soywiz didn't (on WinXPSP2, PHP 5.2.3).The only difference between them is addcslashes (soywiz) instead of preg_quote (jk). They _should_ both work, but for some reason soywiz's didn't for me. So YMMV.However, to make JK's fnmatch() work with the example in the documentation, you also have to strtr the [ and ] in $pattern.<?php$pattern = strtr(preg_quote($pattern, '#'), array('\*' => '.*', '\?' => '.', '\[' => '[', '\]' => ']'));?>And thanks for the functions, guys.
up
-3
phlipping at yahoo dot com
22 years ago
you couls also try this function that I wrote before I found fnmatch:function WildToReg($str){  $s = "";     for ($i = 0; $i < strlen($str); $i++)  {   $c = $str{$i};   if ($c =='?')    $s .= '.'; // any character   else if ($c == '*')        $s .= '.*'; // 0 or more any characters       else if ($c == '[' || $c == ']')    $s .= $c;  // one of characters within []   else    $s .= '\\' . $c;  }  $s = '^' . $s . '$';  //trim redundant ^ or $  //eg ^.*\.txt$ matches exactly the same as \.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