Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Модуль поиска по маске (более совершеный, нежели дельфийский masks)

01.01.2007
unit awMachMask; // © Alexandr Petrovich Sysoev
 
interface
 
uses Classes;
 
///////////////////////////////////////////////////// Работа со списком шаблонов
// Функции предназначены для сопоставления текстов (имен файлов) на
// соответствие заданному шаблону или списку шаблонов.
// Обычно используется для посторения простых фильтров, например аналогичных
// файловым фильтрам программы Total Commander.
//
// Каждый шаблон аналогичен шаблону имен файлов в MS-DOS и MS Windows,
// т.е. может включать "шаблонные" символы '*' и '?' и не может включать
// символ '|'.
// Любой шаблон может быть заключен в двойные кавычки ('''), при этом двойные
// кавычки имеющиеся в шаблоне должны быть удвоены. Если шаблон включает
// символы ';' или ' ' (пробел) то он обязательно должен быть заключен в
// двойные кавычки.
// В списке, шаблоны разделяются символом ';'.
// За первым списком шаблонов, может следовать символ '|', за которым может
// следовать второй список.
// Текст (имя файла) будет считаться соответствующим списку шаблонов только
// если он соответствует хотя бы одному шаблону из первого списка,
// и не соответствует ни одному шаблону из второго списка.
// Если первый список пуст, то подразумевается '*'
//
// Формальное описание синтаксиса списка шаблонов:
//
//    Полный список шаблонов      :: [<список включаемых шаблонов>]['|'<список исключаемых шаблонов>]
//    список включаемых шаблонов  :: <список шаблонов>
//    список исключаемых шаблонов :: <список шаблонов>
//    список шаблонов             :: <шаблон>[';'<шаблон>]
//    шаблон                      :: шаблон аналогичный шаблону имен файлов в
//                                   MS-DOS и MS Windows, т.е. может включать
//                                   "шаблонные" символы '*' и '?' и не может
//                                   включать символ '|'. Шаблон может быть
//                                   заключен в двойные кавычки (''') при этом
//                                   двойные кавычки имеющиеся в шаблоне должны
//                                   быть удвоены. Если шаблон включает символы
//                                   ';' или ' ' (пробел) то он
//                                   обязательно должен быть заключен в двойные
//                                   кавычки.
//
// Например:
//   '*.ini;*.wav'          - соответствует любым файлам с расшиениями 'ini'
//                            или 'wav'
//   '*.*|*.exe'            - соответствует любым файлам, кроме файлов с
//                            расширением 'EXE'
//   '*.mp3;*.wav|?.*;??.*' - соответствует любым файлам с расшиениями 'mp3'
//                            и 'wav' за исключением файлов у которых имя
//                            состоит из одного или двух символов.
//   '|awString.*'          - соответствует любым файлам за исключением файлов
//                            с именем awString и любым расширением.
//
 
Function IsMatchMask (aText, aMask :pChar ) :Boolean;                               overload;
Function IsMatchMask (aText, aMask :String; aFileNameMode :Boolean =True) :Boolean; overload;
  // Выполняют сопоставление текста aText с одним шаблоном aMask.
  // Возвращает True если сопоставление выполнено успешно, т.е. текст
  // aText соответствует шаблону aMask.
  // Если aFileNameModd=True, то объект используется для сопоставления
  // имен файлов с шаблоном. А именно, в этом случае, если aText не
  // содержит символа '.' то он добавляется в конец. Это необходимо для
  // того, чтобы файлы без расширений соответствовали например шаблону '*.*'
 
Function IsMatchMaskList (aText, aMaskList :String; aFileNameMode :Boolean =True): Boolean;
  // Выполняет сопоставление текста aText со списком шаблонов aMaskList.
  // Возвращает True если сопоставление выполнено успешно, т.е. текст
  // aText соответствует списку шаблонов aMaskList.
  // Если aFileNameModd=True, то объект используется для сопоставления
  // имен файлов с шаблоном. А именно, в этом случае, если aText не
  // содержит символа '.' то он добавляется в конец. Это необходимо для
  // того, чтобы файлы без расширений соответствовали например шаблону '*.*'
  //
  // Замечание, если требуется проверка сопоставления нескольких строк одному
  // списку шаблонов, эффективнее будет воспользоваться объектом tMatchMaskList.
 
Type
  tMatchMaskList = class(tObject)
    Private
      fMaskList      :String;
      fCaseSensitive :Boolean;
      fFileNameMode  :Boolean;
 
      fPrepared     :Boolean;
      fIncludeMasks :tStringList;
      fExcludeMasks :tStringList;
 
      procedure SetMaskList      (v :String );
      procedure SetCaseSensitive (v :Boolean);
 
    Public
      constructor Create (Const aMaskList :String ='');
        // Создает объект. Если задан параметр aMaskList, то он присваивается
        // свойству MaskList.
 
      destructor  Destroy;    override;
        // Разрушает объект
 
      procedure PrepareMasks;
        // Осуществляет компиляцию списка шаблонов во внутреннюю структуру
        // используемую при сопоставлении текста.
        // Вызов данного метода не является обязательным и при необходимости
        // будет вызван автоматически.
 
      Function IsMatch (aText :String) :Boolean;
        // Выполняет сопоставление текста aText со списком шаблонов MaskList.
        // Возвращает True если сопоставление выполнено успешно, т.е. текст
        // aText соответствует списку шаблонов MaskList.
 
      Property MaskList      :String   Read fMaskList        Write SetMaskList                     ;
        // Списко шаблонов используемый для сопоставления с текстом
 
      Property CaseSensitive :Boolean  Read fCaseSensitive   Write SetCaseSensitive   default False;
        // Если False (по умолчанию), то при сопоставлении текста будет
        // регистр символов не будет учитываться.
        // Иначе, если True, сопоставление будет проводиться с учетом регистра.
 
      Property FileNameMode :Boolean   Read fFileNameMode    Write fFileNameMode      default True;
        // Если True (по умолчанию), то объект используется для сопоставления
        // имен файлов с шаблоном. А именно, в этом случае, если aText не
        // содержит символа '.' то он добавляется в конец. Это необходимо для
        // того, чтобы файлы без расширений соответствовали например шаблону '*.*'
 
    End;
 
 
implementation
 
uses
  SysUtils
 ;
 
Function IsMatchMask (aText, aMask :pChar ) :Boolean;            overload;
begin
  Result := False;
  While  True  Do begin
    Case  aMask^  of
      '*'  :   // соответствует любому числу любых символов кроме конца строки
        begin
          // переместиться на очередной символ шаблона, при этом, подряд
          // идущие '*' эквивалентны одному, поэтому пропуск всех '*'
          repeat  Inc(aMask);  Until  (aMask^<>'*');
          // если за '*' следует любой символ кроме '?' то он должен совпасть
          // с символом в тексте. т.е. нужно пропустить все не совпадающие,
          // но не далее конца строки
          If  aMask^ <> '?'  then
            While  (aText^ <> #0) And (aText^ <> aMask^)  Do  Inc(aText);
 
          If  aText^ <> #0   Then begin  // не конец строки, значит совпал символ
            // '*' 'жадный' шаблон поэтому попробуем отдать совпавший символ
            // ему. т.е. проверить совпадение продолжения строки с шаблоном,
            // начиная с того-же '*'. если продолжение совпадает, то
            If  IsMatchMask (aText+1, aMask-1)  Then  Break;  // это СОВПАДЕНИЕ
            // продолжение не совпало, значит считаем что здесь закончилось
            // соответствие '*'. Продолжим сопоставление со следующего
            // символа шаблона
            Inc(aMask); Inc(aText);     //   иначе переходим к следующему символу
            End
          Else If  (aMask^ = #0)  Then  // конец строки и конец шаблона
            Break                       //     это СОВПАДЕНИЕ
          Else                          // конец строки но не конец шаблона
            Exit                        //     это НЕ СОВПАДЕНИЕ
        End;
 
      '?'  :   // соответствует любому кроме конца строки
        If (aText^ = #0)  Then          // конец строки
          Exit                          //     это НЕ СОВПАДЕНИЕ
        Else begin                      // иначе
          Inc(aMask); Inc(aText);       //   иначе переходим к следующему символу
        End;
 
      Else     // символ в шаблоне должен совпасть с символом в строке
        If  aMask^ <> aText^  Then      // символы не совпали -
          Exit                          //     это НЕ СОВПАДЕНИЕ
        Else begin                      // совпал очередной символ
          If  (aMask^ = #0)  Then       //   совпавший символ последний -
            Break;                      //     это СОВПАДЕНИЕ
          Inc(aMask); Inc(aText);       //   иначе переходим к следующему символу
        End;
    End;
  End;
  Result := True;
End;
 
Function IsMatchMask (aText, aMask :String; aFileNameMode :Boolean =True) :Boolean;            overload;
begin
  If  aFileNameMode And (Pos('.',aText)=0)  then  aText := aText+'.';
  Result := IsMatchMask(pChar(aText),pChar(aMask));
End;
 
Function IsMatchMaskList (aText, aMaskList :String; aFileNameMode :Boolean =True) :Boolean;
begin
  With  tMatchMaskList.Create(aMaskList)  Do try
    FileNameMode := aFileNameMode;
    Result := IsMatch(aText);
  finally
    Free;
  End;
End;
 
 
/////////////////////////////////////////////////////////// tFileMask
 
 
procedure tMatchMaskList.SetMaskList (v :String );
begin
  If  fMaskList = v  Then  Exit;
  fMaskList := v;
  fPrepared := False;
End;
 
 
procedure tMatchMaskList.SetCaseSensitive  (v :Boolean);
begin
  If  fCaseSensitive = v  Then  Exit;
  fCaseSensitive := v;
  fPrepared      := False;
End;
 
 
constructor tMatchMaskList.Create (Const aMaskList :String);
begin
  MaskList := aMaskList;
  fFileNameMode := True;
 
  fIncludeMasks := TStringList.Create;  With  fIncludeMasks  Do begin
    Delimiter  := ';';
//    Sorted     := True;
//    Duplicates := dupIgnore;
  End;
 
  fExcludeMasks := tStringList.Create;  With  fExcludeMasks  Do begin
    Delimiter  := ';';
//    Sorted     := True;
//    Duplicates := dupIgnore;
  End;
End;
 
 
destructor  tMatchMaskList.Destroy;
begin
  fIncludeMasks.Free;
  fExcludeMasks.Free;
End;
 
 
procedure tMatchMaskList.PrepareMasks;
 
  procedure CleanList(l :tStrings);
  var i :Integer;
  begin
    For  i := l.Count-1  downto  0  Do   If  l[i] = ''  then  l.Delete(i);
  End;
 
var
  s :String;
  i :Integer;
begin
  If  fPrepared  Then  Exit;
 
  If  CaseSensitive  Then
    s := MaskList
  Else
    s := UpperCase(MaskList);
 
  i := Pos('|',s);
  If  i =  0  Then begin
    fIncludeMasks.DelimitedText := s;
    fExcludeMasks.DelimitedText := '';
    End
  Else begin
    fIncludeMasks.DelimitedText := Copy(s,1,i-1);
    fExcludeMasks.DelimitedText := Copy(s,i+1,MaxInt);
  End;
 
  CleanList(fIncludeMasks);
  CleanList(fExcludeMasks);
 
  // если список включаемых шаблонов пуст а
  // список исключаемых шаблонов не пуст, то
  // имеется ввиду что список включаемых шаблонов равен <все файлы>
  If  (fIncludeMasks.Count = 0) And (fExcludeMasks.Count <> 0)  Then
    fIncludeMasks.Add('*');
 
  fPrepared := True;
End;
 
 
Function tMatchMaskList.IsMatch (aText :String) :Boolean;
var
  i :Integer;
begin
  Result := False;
  If  aText = ''  then  Exit;
  If  Not CaseSensitive  Then  aText := UpperCase(aText);
  If  FileNameMode And (Pos('.',aText)=0)  then  aText := aText+'.';
  If  Not fPrepared  Then  PrepareMasks;
 
  // поиск в списке "включаемых" масок до первого совпадения
  For  i := 0  To  fIncludeMasks.Count-1  Do
    If  IsMatchMask(PChar(aText),PChar(fIncludeMasks[i]))  Then begin
      Result := True;
      Break;
    End;
 
  // если совпадение найдено, надо проверить по списку "исключаемых"
  If  Result  Then
    For  i := 0  To  fExcludeMasks.Count-1  Do
      If  IsMatchMask(PChar(aText),PChar(fExcludeMasks[i]))  Then begin
        Result := False;
        Break;
      End;
End;
 
 
 
end.

Автор: Петрович

Взято из https://forum.sources.ru