Moodsky 发表于 2007-2-8 09:52:21

两个不错的通配符比较函数

近日在和朋友讨论 MaskMatch 时偶得2个不错的算法。
函数1 只支持'*','?'模糊匹配。速度比采用递归算法的快近2倍,比TMask方法快很多。
函数2 完全支持正规表达式。速度于之前的相同。(不会正规表达式的朋友慎用)


// ===========================
// Funtion 1
// ===========================

// Check if the string can match the wildcard. It can be used for unicode strings as well!
// C: 2004-07-24 | M: 2004-07-24
function MaskMatch(const aPattern, aSource: string): Boolean;
var
StringPtr, PatternPtr: PChar;
StringRes, PatternRes: PChar;
begin
Result := False;
StringPtr := PChar(UpperCase(aSource));
PatternPtr := PChar(UpperCase(aPattern));
StringRes := nil;
PatternRes := nil;
repeat
    repeat // ohne vorangegangenes "*"
      case PatternPtr^ of
      #0 : begin
               Result := StringPtr^ = #0;
               if Result or (StringRes = nil) or (PatternRes = nil) then Exit;
               StringPtr := StringRes;
               PatternPtr := PatternRes;
               Break;
             end;
      '*': begin
               Inc(PatternPtr);
               PatternRes := PatternPtr;
               Break;
             end;
      '?': begin
               if StringPtr^ = #0 then Exit;
               Inc(StringPtr);
               Inc(PatternPtr);
             end;
      else begin
               if StringPtr^ = #0 then Exit;
               if StringPtr^ <> PatternPtr^ then
               begin
               if (StringRes = nil) or (PatternRes = nil) then Exit;
               StringPtr := StringRes;
               PatternPtr := PatternRes;
               Break;
               end else
               begin
               Inc(StringPtr);
               Inc(PatternPtr);
               end;
             end;
      end;
    until False;

    repeat // mit vorangegangenem "*"
      case PatternPtr^ of
      #0 : begin
               Result := True;
               Exit;
             end;
      '*': begin
               Inc(PatternPtr);
               PatternRes := PatternPtr;
             end;
      '?': begin
               if StringPtr^ = #0 then Exit;
               Inc(StringPtr);
               Inc(PatternPtr);
             end;
      else begin
               repeat
               if StringPtr^ = #0 then Exit;
               if StringPtr^ = PatternPtr^ then Break;
               Inc(StringPtr);
               until False;
               Inc(StringPtr);
               StringRes := StringPtr;
               Inc(PatternPtr);
               Break;
             end;
      end;
    until False;
until False;
end;


// ===========================
// Funtion 2
// ===========================

function _MatchPattern(aPattern, aSource: PChar): Boolean;
begin
Result := True;
while (True) do
begin
    case aPattern of
      #0 : begin
             //End of pattern reached.
             Result := (aSource = #0); //TRUE if end of aSource.
             Exit;
         end;

      '*': begin //Match zero or more occurances of any char.
             if (aPattern = #0) then
             begin
               //Match any number of trailing chars.
               Result := True;
               Exit;
             end else
            Inc(aPattern);

             while (aSource <> #0) do
             begin
               //Try to match any substring of aSource.
               if (_MatchPattern(aSource, aPattern)) then
               begin
               Result := True;
               Exit;
               end;

               //Continue testing next char...
               Inc(aSource);
             end;
         end;

      '?': begin //Match any one char.
             if (aSource = #0) then
             begin
               Result := False;
               Exit;
             end;

             //Continue testing next char...
             Inc(aSource);
             Inc(aPattern);
         end;

      '[': begin //Match given set of chars.
             if (aPattern in [#0,'[',']']) then
             begin
               //Invalid Set - So no match.
               Result := False;
               Exit;
             end;

             if (aPattern = '^') then
             begin
               //Match for exclusion of given set...
               Inc(aPattern, 2);
               Result := True;
               while (aPattern <> ']') do
               begin
               if (aPattern = '-') then
               begin
                   //Match char exclusion range.
                   if (aSource >= aPattern) and (aSource <= aPattern) then
                   begin
                     //Given char failed set exclusion range.
                     Result := False;
                     Break;
                   end else
                     Inc(aPattern, 3);
               end else
               begin
                   //Match individual char exclusion.
                   if (aSource = aPattern) then
                   begin
                     //Given char failed set element exclusion.
                     Result := False;
                     Break;
                   end else
                  Inc(aPattern);
               end;
               end;
             end else
             begin
               //Match for inclusion of given set...
               Inc(aPattern);
               Result := False;
               while (aPattern <> ']') do
               begin
               if (aPattern = '-') then
               begin
                   //Match char inclusion range.
                   if (aSource >= aPattern) and (aSource <= aPattern) then
                   begin
                     //Given char matched set range inclusion.
                     // Continue testing...
                     Result := True;
                     Break;
                   end else
                  Inc(aPattern, 3);
               end else
               begin
                   //Match individual char inclusion.
                   if (aSource = aPattern) then
                   begin
                     //Given char matched set element inclusion.
                     // Continue testing...
                     Result := True;
                     Break;
                   end else
                     Inc(aPattern);
               end;
               end;
             end;

             if (Result) then
             begin
               //Match was found. Continue further.
               Inc(aSource);
               //Position Pattern to char after "]"
               while (aPattern <> ']') and (aPattern <> #0) do Inc(aPattern);
               if (aPattern = #0) then
               begin
               //Invalid Pattern - missing "]"
               Result := False;
               Exit;
               end else
               Inc(aPattern);
             end else
               Exit;
         end;

      else begin //Match given single char.
             if (aSource <> aPattern) then
             begin
               Result := False;
               Break;
             end;

             //Continue testing next char...
             Inc(aSource);
             Inc(aPattern);
         end;
    end;
end;
end;

function MatchPattern(const aPattern, aSource: string): Boolean;
begin
Result := _MatchPattern(PChar(aPattern), PChar(aSource));
end;

[ 本帖最后由 Moodsky 于 2007-2-8 10:13 编辑 ]
页: [1]
查看完整版本: 两个不错的通配符比较函数