php適用於windows的fnmatch(匹配函數),可匹配中文。

 

該貼中有兩種方法可以實現fnmatch函數,現貼如下:

 

function fnmatch($pattern, $string)         //$pattern匹配式, $string被匹配的字符串
{
    $starStack = array();                   //創建記錄pattern開始位置的棧,這個作用是像編輯器的後退
    $sstrStack = array();                   //創建記錄$string開始位置的棧
    $countStack = 0;                        //棧大小,用一個同步記錄棧大小,減少count()時所耗的時間
    $ptnStart = strlen($pattern) - 1;       //定位匹配式最後一個字符, 算法是從字符串後面開始匹配
    $strStart = strlen($string) - 1;        //定位字符串的最好一個字符
    for(; 0 <= $strStart; $strStart --)     //開始匹配循環, 每匹配一個字符, $strStart就往前移一個字符
    {
        $sc = $string{$strStart};           //取得當前在比較的字符
        $pc = ($ptnStart < 0) ? '' : $pattern{$ptnStart};//取得匹配式當前的字符,已到結束位置,給個空
        if($sc !== $pc)
        {                                   //當兩個字符不相同時, 就要進行一些匹配式特殊字符的比較
            if($pc === '*')                 //如果匹配式當前字符是*號, 進行*號匹配
            {
                while($ptnStart > 0 && ($pc = $pattern{$ptnStart - 1}) === '*')
                    $ptnStart --;           //while這段是去除幾個連續的*號, 並嘗試和取得下一個字符
                if($ptnStart > 0 && ($pc === $sc || $pc === '?'))//比較下個字符是否相同或是?號
                {                           //如果下一個字符匹配成功
                    $starStack[$countStack] = $ptnStart;//保存這個*號的位置
                    $sstrStack[$countStack] = $strStart;//保存$string開始位置
                    $countStack ++;         //棧向下移一
                    $ptnStart -= 2;         //匹配式定位,前移兩位,分別是當前*號位和已經匹配的一個
                    continue;               //進行下一次循環
                }
            }
            elseif($pc === '?')             //如果匹配式當前字符是?號, 進行?號匹配
            {
                $ptnStart --;               //?號匹配是字符串同步前移一個位置
            }
            elseif($countStack > 0)         //如果不是通配符,檢查棧中是否有保存上一個*號的位置
            {                               //有就還原此*號位置, 回到上一個*號處再次進行匹配
                $countStack --;
                $ptnStart = $starStack[$countStack];//還原*號位置
                $strStart = $sstrStack[$countStack];//還原$string開始位置
            }
            else 
            {
                return false;               //以上情況都沒有的話, 匹配失敗, 返回flase
            }
        }
        else
        {
            $ptnStart --;                   //字符串位置和匹配式位置上相同,前移一位,繼續下個匹配
        }
    }                                       //匹配循環結束
    if($ptnStart === -1)                    //剛好匹配式的位置也結束, 則匹配成功, 返回true
    {
        return true;
    }
    elseif($ptnStart >= 0)                  //匹配式並沒有結束, 還有一些沒有匹配
    {
        while($ptnStart > 0 && $pattern{$ptnStart} === '*')//檢查剩下的是不是都是*號,去除這些*號
            $ptnStart --;
        if($pattern{$ptnStart} === '*')     //最後的隻有一個*號結束的話, 就匹配成功, 返回true
            return true;
        else
            return false;                   //否則, 返回false
    }
    return false;
}

if (!function_exists('fnmatch')) {
        function fnmatch($pattern, $string) {
            return @preg_match('/^' . strtr(addcslashes($pattern, '.+^$(){}=!<>|'), array('*' => '.*', '?' => '.?')) . '$/i', $string);
        }
    }

這兩個方法都可以實現,但由於我要匹配的有包含中文的,比如

 

我愛中國

匹配 我愛??

就無法實現瞭,因為“中國”這個字符算4個字符,假如 匹配 我愛???? 應該就沒問題瞭,但是這樣對於我們來說使用非常的不方便,於是我改瞭一個第一個函數的實現,使用mb_strlen的方法來統計和分割字符,實現如下:

 

function fnmatch($pattern, $string)         //$pattern匹配式, $string被匹配的字符串
{
	$encoding = gb2312;					//根據自己的頁面的編碼,來定義這個編碼
    $starStack = array();                   //創建記錄pattern開始位置的棧,這個作用是像編輯器的後退
    $sstrStack = array();                   //創建記錄$string開始位置的棧
    $countStack = 0;                        //棧大小,用一個同步記錄棧大小,減少count()時所耗的時間
    $ptnStart = mb_strlen($pattern, $encoding) - 1;       //定位匹配式最後一個字符, 算法是從字符串後面開始匹配
    $strStart = mb_strlen($string, $encoding) - 1;        //定位字符串的最好一個字符
    for(; 0 <= $strStart; $strStart --)     //開始匹配循環, 每匹配一個字符, $strStart就往前移一個字符
    {
		$sc = mb_substr($string, $strStart, 1, $encoding);           //取得當前在比較的字符
		$pc = ($ptnStart < 0) ? '' : mb_substr($pattern, $ptnStart, 1, $encoding);//取得匹配式當前的字符,已到結束位置,給個空
        if($sc !== $pc)
        {                                   //當兩個字符不相同時, 就要進行一些匹配式特殊字符的比較
            if($pc === '*')                 //如果匹配式當前字符是*號, 進行*號匹配
            {
                while($ptnStart > 0 && ($pc = mb_substr($pattern, $ptnStart-1, 1, $encoding)) === '*')
                    $ptnStart --;           //while這段是去除幾個連續的*號, 並嘗試和取得下一個字符
                if($ptnStart > 0 && ($pc === $sc || $pc === '?'))//比較下個字符是否相同或是?號
                {                           //如果下一個字符匹配成功
                    $starStack[$countStack] = $ptnStart;//保存這個*號的位置
                    $sstrStack[$countStack] = $strStart;//保存$string開始位置
                    $countStack ++;         //棧向下移一
                    $ptnStart -= 2;         //匹配式定位,前移兩位,分別是當前*號位和已經匹配的一個
                    continue;               //進行下一次循環
                }
            }
            elseif($pc === '?')             //如果匹配式當前字符是?號, 進行?號匹配
            {
                $ptnStart --;               //?號匹配是字符串同步前移一個位置
            }
            elseif($countStack > 0)         //如果不是通配符,檢查棧中是否有保存上一個*號的位置
            {                               //有就還原此*號位置, 回到上一個*號處再次進行匹配
                $countStack --;
                $ptnStart = $starStack[$countStack];//還原*號位置
                $strStart = $sstrStack[$countStack];//還原$string開始位置
            }
            else 
            {
                return false;               //以上情況都沒有的話, 匹配失敗, 返回flase
            }
        }
        else
        {
            $ptnStart --;                   //字符串位置和匹配式位置上相同,前移一位,繼續下個匹配
        }
    }                                       //匹配循環結束
    if($ptnStart === -1)                    //剛好匹配式的位置也結束, 則匹配成功, 返回true
    {
        return true;
    }
    elseif($ptnStart >= 0)                  //匹配式並沒有結束, 還有一些沒有匹配
    {
        while($ptnStart > 0 && mb_substr($pattern, $ptnStart, 1, $encoding) === '*')//檢查剩下的是不是都是*號,去除這些*號
            $ptnStart --;
        if(mb_substr($pattern, $ptnStart, 1, $encoding) === '*')     //最後的隻有一個*號結束的話, 就匹配成功, 返回true
            return true;
        else
            return false;                   //否則, 返回false
    }
    return false;
}

實現完畢,可完美匹配中文瞭。

 

You May Also Like