php适用于windows的fnmatch(匹配函数),可匹配中文。

最近在写项目时,需要用到通配符匹配一些字符串,需要类似*匹配所有字符,?匹配单个字符的做法,在php中找到了fnmatch函数可使用,但发现它不能使用在windows的环境中

该贴中有两种方法可以实现fnmatch函数,现贴如下:

 

[php][/php] view plaincopy

  1. function fnmatch($pattern, $string)         //$pattern匹配式, $string被匹配的字符串
  2. {
  3.     $starStack = array();                   //创建记录pattern开始位置的栈,这个作用是像编辑器的后退
  4.     $sstrStack = array();                   //创建记录$string开始位置的栈
  5.     $countStack = 0;                        //栈大小,用一个同步记录栈大小,减少count()时所耗的时间
  6.     $ptnStart = strlen($pattern) – 1;       //定位匹配式最后一个字符, 算法是从字符串后面开始匹配
  7.     $strStart = strlen($string) – 1;        //定位字符串的最好一个字符
  8.     for(; 0 <= $strStart; $strStart –)     //开始匹配循环, 每匹配一个字符, $strStart就往前移一个字符
  9.     {
  10.         $sc = $string{$strStart};           //取得当前在比较的字符
  11.         $pc = ($ptnStart < 0) ? ” : $pattern{$ptnStart};//取得匹配式当前的字符,已到结束位置,给个空
  12.         if($sc !== $pc)
  13.         {                                   //当两个字符不相同时, 就要进行一些匹配式特殊字符的比较
  14.             if($pc === ‘*’)                 //如果匹配式当前字符是*号, 进行*号匹配
  15.             {
  16.                 while($ptnStart > 0 && ($pc = $pattern{$ptnStart – 1}) === ‘*’)
  17.                     $ptnStart –;           //while这段是去除几个连续的*号, 并尝试和取得下一个字符
  18.                 if($ptnStart > 0 && ($pc === $sc || $pc === ‘?’))//比较下个字符是否相同或是?号
  19.                 {                           //如果下一个字符匹配成功
  20.                     $starStack[$countStack] = $ptnStart;//保存这个*号的位置
  21.                     $sstrStack[$countStack] = $strStart;//保存$string开始位置
  22.                     $countStack ++;         //栈向下移一
  23.                     $ptnStart -= 2;         //匹配式定位,前移两位,分别是当前*号位和已经匹配的一个
  24.                     continue;               //进行下一次循环
  25.                 }
  26.             }
  27.             elseif($pc === ‘?’)             //如果匹配式当前字符是?号, 进行?号匹配
  28.             {
  29.                 $ptnStart –;               //?号匹配是字符串同步前移一个位置
  30.             }
  31.             elseif($countStack > 0)         //如果不是通配符,检查栈中是否有保存上一个*号的位置
  32.             {                               //有就还原此*号位置, 回到上一个*号处再次进行匹配
  33.                 $countStack –;
  34.                 $ptnStart = $starStack[$countStack];//还原*号位置
  35.                 $strStart = $sstrStack[$countStack];//还原$string开始位置
  36.             }
  37.             else
  38.             {
  39.                 return false;               //以上情况都没有的话, 匹配失败, 返回flase
  40.             }
  41.         }
  42.         else
  43.         {
  44.             $ptnStart –;                   //字符串位置和匹配式位置上相同,前移一位,继续下个匹配
  45.         }
  46.     }                                       //匹配循环结束
  47.     if($ptnStart === -1)                    //刚好匹配式的位置也结束, 则匹配成功, 返回true
  48.     {
  49.         return true;
  50.     }
  51.     elseif($ptnStart >= 0)                  //匹配式并没有结束, 还有一些没有匹配
  52.     {
  53.         while($ptnStart > 0 && $pattern{$ptnStart} === ‘*’)//检查剩下的是不是都是*号,去除这些*号
  54.             $ptnStart –;
  55.         if($pattern{$ptnStart} === ‘*’)     //最后的只有一个*号结束的话, 就匹配成功, 返回true
  56.             return true;
  57.         else
  58.             return false;                   //否则, 返回false
  59.     }
  60.     return false;
  61. }

 

[php][/php] view plaincopy

  1. if (!function_exists(‘fnmatch’)) {
  2.         function fnmatch($pattern, $string) {
  3.             return @preg_match(‘/^’ . strtr(addcslashes($pattern, ‘\.+^$(){}=!<>|’), array(‘*’ => ‘.*’, ‘?’ => ‘.?’)) . ‘$/i’, $string);
  4.         }
  5.     }

这两个方法都可以实现,但由于我要匹配的有包含中文的,比如

 

我爱中国

匹配 我爱??

就无法实现了,因为“中国”这个字符算4个字符,假如 匹配 我爱???? 应该就没问题了,但是这样对于我们来说使用非常的不方便,于是我改了一个第一个函数的实现,使用mb_strlen的方法来统计和分割字符,实现如下:

 

[php][/php] view plaincopy

  1. function fnmatch($pattern, $string)         //$pattern匹配式, $string被匹配的字符串
  2. {
  3.     $encoding = “gb2312”;                   //根据自己的页面的编码,来定义这个编码
  4.     $starStack = array();                   //创建记录pattern开始位置的栈,这个作用是像编辑器的后退
  5.     $sstrStack = array();                   //创建记录$string开始位置的栈
  6.     $countStack = 0;                        //栈大小,用一个同步记录栈大小,减少count()时所耗的时间
  7.     $ptnStart = mb_strlen($pattern, $encoding) – 1;       //定位匹配式最后一个字符, 算法是从字符串后面开始匹配
  8.     $strStart = mb_strlen($string, $encoding) – 1;        //定位字符串的最好一个字符
  9.     for(; 0 <= $strStart; $strStart –)     //开始匹配循环, 每匹配一个字符, $strStart就往前移一个字符
  10.     {
  11.         $sc = mb_substr($string, $strStart, 1, $encoding);           //取得当前在比较的字符
  12.         $pc = ($ptnStart < 0) ? ” : mb_substr($pattern, $ptnStart, 1, $encoding);//取得匹配式当前的字符,已到结束位置,给个空
  13.         if($sc !== $pc)
  14.         {                                   //当两个字符不相同时, 就要进行一些匹配式特殊字符的比较
  15.             if($pc === ‘*’)                 //如果匹配式当前字符是*号, 进行*号匹配
  16.             {
  17.                 while($ptnStart > 0 && ($pc = mb_substr($pattern, $ptnStart-1, 1, $encoding)) === ‘*’)
  18.                     $ptnStart –;           //while这段是去除几个连续的*号, 并尝试和取得下一个字符
  19.                 if($ptnStart > 0 && ($pc === $sc || $pc === ‘?’))//比较下个字符是否相同或是?号
  20.                 {                           //如果下一个字符匹配成功
  21.                     $starStack[$countStack] = $ptnStart;//保存这个*号的位置
  22.                     $sstrStack[$countStack] = $strStart;//保存$string开始位置
  23.                     $countStack ++;         //栈向下移一
  24.                     $ptnStart -= 2;         //匹配式定位,前移两位,分别是当前*号位和已经匹配的一个
  25.                     continue;               //进行下一次循环
  26.                 }
  27.             }
  28.             elseif($pc === ‘?’)             //如果匹配式当前字符是?号, 进行?号匹配
  29.             {
  30.                 $ptnStart –;               //?号匹配是字符串同步前移一个位置
  31.             }
  32.             elseif($countStack > 0)         //如果不是通配符,检查栈中是否有保存上一个*号的位置
  33.             {                               //有就还原此*号位置, 回到上一个*号处再次进行匹配
  34.                 $countStack –;
  35.                 $ptnStart = $starStack[$countStack];//还原*号位置
  36.                 $strStart = $sstrStack[$countStack];//还原$string开始位置
  37.             }
  38.             else
  39.             {
  40.                 return false;               //以上情况都没有的话, 匹配失败, 返回flase
  41.             }
  42.         }
  43.         else
  44.         {
  45.             $ptnStart –;                   //字符串位置和匹配式位置上相同,前移一位,继续下个匹配
  46.         }
  47.     }                                       //匹配循环结束
  48.     if($ptnStart === -1)                    //刚好匹配式的位置也结束, 则匹配成功, 返回true
  49.     {
  50.         return true;
  51.     }
  52.     elseif($ptnStart >= 0)                  //匹配式并没有结束, 还有一些没有匹配
  53.     {
  54.         while($ptnStart > 0 && mb_substr($pattern, $ptnStart, 1, $encoding) === ‘*’)//检查剩下的是不是都是*号,去除这些*号
  55.             $ptnStart –;
  56.         if(mb_substr($pattern, $ptnStart, 1, $encoding) === ‘*’)     //最后的只有一个*号结束的话, 就匹配成功, 返回true
  57.             return true;
  58.         else
  59.             return false;                   //否则, 返回false
  60.     }
  61.     return false;
  62. }

实现完毕,可完美匹配中文了。

标签