Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.22.77.149] |
|
Сообщ.
#1
,
|
|
|
Регулярные выражения Введение. Регулярные выражения - это один из способов поиска подстрок (соответствий) в строках. Поиск осуществляется по заданному шаблону, в соответствии с определенными правилами. Самостоятельное создание подобного парсера занятие довольно трудоемкое и долгое, а потому лучше использовать уже существующие. Но, когда мне потребовалось генерировать HTML-файлы, на основе существующих текстовых файлов, неожиданно столкнулся с проблемой, а именно с отсутствием выбора. То есть выбор был, но довольно скудный, который я остановил на VBScript Regular Expression, поставляемый в составе IE, в файле vbscript.dll, начиная с Win9x. Сразу замечу, что существуют две версии Regular Expression. В Win9x, по умолчанию, используется версия 1.0, достаточно функциональная, но не полная. Следующая версия интерфейса (5.5, а не 2.0, как мог кто-то подумать) вышла вместе с IE 5.5 и обладает уже приемлемым функционалом, пригодным для любых задач. Я рассмотрю обе, указывая их отличия по мере необходимости. Заранее прошу прощения за возможные неточности в терминологии, но я не силен в теории, тем более ООП Инструменты. VBScript Regular Expression написан с использованием технологии COM, поэтому выбор ассемблера напрашивается сам собой - Turbo Assembler, имеющий удобную поддержку ООП. Очень сильно помогла утилита OLEVIEW, из состава Visual Studio, благодаря которой я смог узнать структуру всех используемых интерфейсов и параметры методов. Thanks и создателю OllyDbg, за прекрасный инструмент, без которого данная статья навряд ли бы появилась. Инициализация. Для начала любой работы с COM-объектами необходимо инициализировать COM в текущем потоке, создать объект класса и запросить ссылку на экземпляр нужного интерфейса. После работы необходимо уничтожить созданные объекты и деинсталлировать COM в текущем потоке. Как известно, для создания экземпляра класса, а так же для получения ссылок на любые интерфейсы необходимо знать их уникальные идентификаторы - GUID, вот и начнем с их объявления. Идентификатор класса можно найти в реестре, в ветке \HKEY_CLASSES_ROOT\CLSID\. Идентификаторы интерфейсов легко определить с помощью OLEVIEW. Таким образом, имеем все необходимое для их объявления: ; This macro is from Morten Elling's TaCOM examples, thanks to the author. macro GUID gName: req, gString:req local @@byte label gName byte irp t, <8,6,4,2, 13,11, 18,16, 21,23, 26,28,30,32,34,36> @@byte SUBSTR <gString>, t, 2 @@byte CATSTR <0>, @@byte, <h> db @@byte endm endm dataseg ; ID класса GUID CLSID_IRegExp {3F4DACA4-160D-11D2-A8E9-00104B365C9F} ; ID интерфейсов GUID IID_IRegExp {3F4DACA0-160D-11D2-A8E9-00104B365C9F} ; v 1.0 GUID IID_IRegExp2 {3F4DACB0-160D-11D2-A8E9-00104B365C9F} ; v 5.5 Теперь можно смело переходить к инициализации COM и получению интерфейсов. Оформим это в виде подпрограмм, чтобы больше не возвращаться к этому: dataseg COMInstalled dd 0 codeseg ; запрашивает интерфейс @@IID класса @@CLSID ; на входе: ; @@CLSID - GUID запрашиваемого класса ; @@IID - GUID запрашиваемого интерфейса ; на выходе: ; eax - ссылка на интерфейс, или NULL в случае ошибки proc CreateObject uses edx, @@CLSID, @@IID cmp [COMInstalled], 0 jne @@create call CoInitialize, 0 ; инициализируем COM @@create: inc [COMInstalled] ; создаем объект класса и запрашиваем интерфейс push eax ; выделяем временный буфер, под указатель на запрашиваемый интерфейс call CoCreateInstance, [@@CLSID], 0, 5, [@@IID], esp pop edx ; edx = LPVOID * ppv or eax, eax jns @@done ; ошибка, уничтожаем COM call DestroyObject, 0 xor edx, edx @@done: xchg eax, edx ; возвращаем результат в EAX ret endp ; уничтожает объект и, в случае необходимости, деинсталлирует COM в текущем потоке ; на входе: ; @@lpObj - ссылка на экземпляр объекта ; если уничтожили последний объект, то автоматом вызовится CoUninitialize proc DestroyObject uses edx, @@lpObj cmp [COMInstalled], 0 je @@done mov edx, [@@lpObj] or edx, edx jz @@isuninst call IUnknown edx METHOD IUnknown:Release USES ds:eax, edx @@isuninst: dec [COMInstalled] jnz @@done call CoUninitialize @@done: ret endp Строки. Поскольку регулярные выражения применяются в основном для обработки строк, то сразу определимся с их форматом, для лучшего понимания последующих примерчиков. Дело в том, что VBScript, как не трудно догадаться из названия, использует тот же тип строк, что и Visual Basic, так называемый BSTR. Это обычные Unicode-строки, со счетчиком длины, который имеет размер в двойное слово. Но, в отличие от привычных Паскаль-строк, указатель на строку указывает на первый символ строки, а не на счетчик длины! Таким образом, их можно использовать и как строки с завершающим нулем. Но нам удобнее работать в ANSI-режиме, преимущества которого очевидны во многих случаях, особенно при интенсивной работе с файлами и консолью, поэтому создадим парочку подпрограмм, для конвертирования ANSI<-->BSTR: ; конвертирует ANSI строку в BSTR(unicode) ; возвращает указатель на сформированную строку proc ANSItoBSTR uses ebx, @@ansistr local @@lena, @@lenw call lstrlenA, [@@ansistr] mov [@@lena], eax inc eax ; резервируем место для завершающего нуля shl eax, 1 ; eax = eax*2 mov [@@lenw], eax call GlobalAlloc, 0, eax or eax, eax jz @@done mov ebx, eax call MultiByteToWideChar, 0, 0, [@@ansistr], [@@lena], eax, [@@lenw] call SysAllocStringLen, ebx, eax push eax call GlobalFree, ebx pop eax @@done: ret endp ; конвертирует BSTR строку в ANSI ; возвращает указатель на сформированную строку proc BSTRtoANSI uses edx, @@lpBStr local @@len call lstrlenW, [@@lpBStr] inc eax ; резервируем место для завершающего нуля mov [@@len], eax call GlobalAlloc, 0, eax or eax, eax jz @@done push eax xor edx, edx call WideCharToMultiByte, edx, edx, [@@lpBStr], -1, eax, [@@len], edx, edx pop eax @@done: ret endp Интерфейсы. Regular Expression предоставляет четыре интерфейса. Вся работа по поиску и замене сосредоточена в одном, а остальные три являются вспомогательными: IRegExp - основной интерфейс, реализующий все функции поиска, замены и выборки соответствий по заданному шаблону. IMatchCollection - вспомогательный интерфейс, содержит коллекцию найденных соответствий, хранимых в виде массива объектов IMatch. IMatch - вспомогательный интерфейс, используется для хранения одного найденного соответствия. ISubMatches - вспомогательный интерфейс, появился в версии 5.5, содержит коллекцию субсоответствий, то есть как бы соответствие в соответствии. Все эти интерфейсы являются потомками интерфейса IDispatch, который в свою очередь является потомком IUnknown, базового интерфейса всех объектов, согласно COM-технологии. Так что вполне логично начать именно с их объявления: struc IUnknown METHOD { virtual QueryInterface ; ([in] GUID* riid, [out] void** ppvObj) virtual AddRef ; () virtual Release ; () } ends struc IDispatch IUnknown METHOD { virtual GetTypeInfoCount ; ([out] unsigned int* pctinfo) virtual GetTypeInfo ; [in] unsigned int itinfo, [in] unsigned long lcid, [out] void** pptinfo) virtual GetIDsOfNames ; virtual Invoke ; } ends Затем объявляем объект IDispatch, но уже в качестве потомка IUnknown, что автоматом добавит в его таблицу VMT все методы IUnknown. Еще отмечу, что параметры методов указаны в комментариях, в том виде, в котором их выдает OLEVIEW, что довольно удобно, но не дает полной картины правильной передачи параметров в методы. В квадратных скобках находится уточнение о типе параметра - входящие(in) или возвращаемые(out) значения. Более подробно описывать эти интерфейсы нет смысла, первый описан в любом учебнике по COM-технологии, а второй напрямую не относится к теме и используется для вызова методов по их именам, что нам не пригодится. Интерфейс IRegExp. Это основной интерфейс, предоставляющий базовые методы для работы с регулярными выражениями. Всего интерфейс предоставляет три (для версии 5.5 - четыре) свойства и три метода. Свойства: Pattern - строка с регулярным выражением, являющаяся шаблоном для поиска подстрок. Обязательно должна быть задана до первого использования любых методов объекта. IgnoreCase - булевое значение. Если задано TRUE(-1), то при поиске соответствий игнорируется регистр символов. По умолчанию имеет значение FALSE, как и остальные свойства. Global - булевое значение. Если TRUE, то будут искаться все возможные соответствия в строке. При значении FALSE поиск остановится после первого найденного соответствия. Multiline - булевое значение, появилось в версии 5.5. Значение TRUE соответствует многострочному поиску, т.е. учитываются переносы строк. В противном случае исходная строка рассматривается просто как набор символов. В принципе можно обойтись и без этого свойства, указывая начало и конец строк в шаблоне поиска, но все же отказываться от него не стоит, так проще. Методы: Test - ищет в строке соответствия заданному шаблону поиска, в случае успеха возвращает TRUE. Execute - то же, что и предыдущий метод, но с одним существенным отличием - все найденные соответствия будут помещены в коллекцию IMatchCollection. Replace - ищет в строке соответствия заданному шаблону и заменяет их на строку, заданную в параметрах. При замене возможна примитивная обработка результатов поиска. Учитывая тот факт, что свойства являются ни чем иным как простыми методами, для чтения/записи некоторых внутренних данных объекта, структура интерфейса будет выглядеть следующим образом: ; version 1.0 struc IRegExp IDispatch METHOD { virtual GetPattern ; [out, retval] BSTR* pPattern virtual SetPattern ; [in] BSTR pPattern virtual GetIgnoreCase ; [out, retval] VARIANT_BOOL* pIgnoreCase virtual SetIgnoreCase ; [in] VARIANT_BOOL pIgnoreCase virtual GetGlobal ; [out, retval] VARIANT_BOOL* pGlobal virtual SetGlobal ; [in] VARIANT_BOOL pGlobal virtual Execute ; [in] BSTR sourceString, [out, retval] IDispatch** ppMatches virtual Tests ; [in] BSTR sourceString, [out, retval] VARIANT_BOOL* pMatch virtual Replace ; [in] BSTR sourceString, [in] VARIANT replaceVar, [out, retval] BSTR* pDestString } ends ; version 5.5 struc IRegExp2 IDispatch METHOD { virtual GetPattern ; [out, retval] BSTR* pPattern virtual SetPattern ; [in] BSTR pPattern virtual GetIgnoreCase ; [out, retval] VARIANT_BOOL* pIgnoreCase virtual SetIgnoreCase ; [in] VARIANT_BOOL pIgnoreCase virtual GetGlobal ; [out, retval] VARIANT_BOOL* pGlobal virtual SetGlobal ; [in] VARIANT_BOOL pGlobal virtual GetMultiline ; [out, retval] VARIANT_BOOL* pMultiline virtual SetMultiline ; [in] VARIANT_BOOL pMultiline virtual Execute ; [in] BSTR sourceString, [out, retval] IDispatch** ppMatches virtual Tests ; [in] BSTR sourceString, [out, retval] VARIANT_BOOL* pMatch virtual Replace ; [in] BSTR sourceString, [in] VARIANT replaceVar, [out, retval] BSTR* pDestString } ends Свойства интерфейса IRegExp. Кто внимательно читал статью, тот помнит, что свойства интерфейса влияют на условия поиска соответствий. Изменять их значения можно с помощью методов SetGlobal, SetIgnoreCase и SetMultiline, которые принимают один параметр типа BOOL. Вот и первый сюрприз Вижен Бейсика - булевые типы имеют размер в слово, но при использовании их в качестве параметров на стэке, используются разумеется двойные слова! Вызовы методов выглядят следующим образом: call IRegExp2 ebx METHOD SetGlobal USES ds:eax, ebx, -1 call IRegExp2 ebx METHOD SetIgnoreCase USES ds:eax, ebx, -1 call IRegExp2 ebx METHOD SetMultiline USES ds:eax, ebx, -1 CALL <class> <instance> METHOD [class:]<method> [USES segreg:reg] [mode][, parameters] где: class - имя объекта. instance - указатель на экземпляр объекта. method - разумеется имя вызываемого метода. USES - определяет временный регистр, используемый для вызова виртуальных методов. mode - соглашение о модели вызова подпрограммы (C, Pascal, StdCall и т.д.), позволяющее переопределить текущую модель (заданную директивой MODEL) для данного вызова. parameters - параметры, разделенные либо запятой, либо пробелами, пишущиеся, как и в языках высокого уровня, слева направо, но ложиться в стэк они будут в порядке, определенным директивой MODEL, либо в порядке заданным mode. Думаю тут все ясно, осталось только уточнить назначение ключевого слова USES. Это слово сам Том Сван, в своей замечательной книге "Освоение Turbo Assembler" истолковал неверно, сделав совершенно ошибочные выводы, оно и понятно, документация на tasm была крайне кривой Для его правильного толкования рассмотрим вызов виртуального метода: ; ebx - указатель на экземпляр объекта mov eax, [ebx] ; eax - адрес таблицы виртуальных методов (VMT) push 0 ; параметры push ebx ; push Self/This call [eax+смещение_метода_в_VMT] Таким образом, вызов: call IRegExp2 ebx METHOD SetGlobal USES ds:edx, ebx, -1 mov edx, [ebx] ; edx = addr VMT push -1 push ebx ; push Self|This call [ds:edx+30h] ; call [VMT+30h] Свойства можно не только задавать, но и получить их состояние вызовом методов GetGlobal, GetIgnoreCase и GetMultiline, которым передается указатель на переменную типа BOOL. Например, получить состояния свойства Global можно так: push eax ; выделяем временный буфер в стеке call IRegExp2 ebx METHOD GetGlobal USES ds:eax, ebx, esp pop edx ; dx - полученное значение свойства объекта (word) Осталось рассмотреть еще одно свойство - Pattern. Это свойство имеет тип BSTR и предназначено для хранения шаблона поиска. Шаблон задается через метод SetPattern, имеющий один параметр - указатель на BSTR-строку шаблона. Получить указатель на текущий шаблон можно с помощью метода GetPattern, которому передается указатель на двойное слово, куда и будет записан адрес BSTR-строки шаблона. Например: dataseg szPattern db '^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$', 0 codeseg ; ..... ; задаем шаблон поиска соответствий call ANSItoBSTR, offset szPattern ; готовим BSTR-строку шаблона поиска соответствий call IRegExp2 ebx METHOD SetPattern USES ds:edx, ebx, eax ; ..... ; получаем текущий шаблон поиска соответствий push eax ; выделяем временный буфер под указатель на строку шаблона call IRegExp2 ebx METHOD GetPattern USES ds:eax, ebx, esp pop eax ; eax - адрес BSTR-строки шаблона Метод Test. Для тестирования строки на соответствие шаблону предназначен метод Test. Он просто ищет соответствие по заданному шаблону и возвращает булевое значение TRUE, если соответствие найдено, или FALSE, если таковых не имеется. Метод принимает два параметра, указатель на строку, в которой ищется соответствие и указатель на переменную типа BOOL, в которую будет записан результат поиска. Шаблон поиска уже должен находиться в свойстве Pattern. В качестве примера можно рассмотреть тестирование строки на предмет соответствия ее содержимого вещественному или целому числу: ; file: test.asm ideal p586 model flat, stdcall locals @@ %NOINCL include 'kernel32.inc' include 'regexpr.inc' include 'ole32.inc' include 'oleaut32.inc' includelib 'imp32i.lib' dataseg ; ID класса и интерфейсов GUID CLSID_IRegExp {3F4DACA4-160D-11D2-A8E9-00104B365C9F} GUID IID_IRegExp {3F4DACA0-160D-11D2-A8E9-00104B365C9F} ; v 1.0 GUID IID_IRegExp2 {3F4DACB0-160D-11D2-A8E9-00104B365C9F} ; v.5.5 ; тестовые строки, содержащие "числа" szTest1 db '+12E-2', 0 szTest2 db '02+E-a', 0 ; шаблон для поиска szPatNum db '^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$', 0 codeseg Begin: ; program entry point ... ; инициализируем COM и получаем экземпляр объекта RegExp2 call CreateObject, lpCLSID_IRegExp, lpIID_IRegExp2 _error nz, "Sorry, this programm request VBScript Regular Expression ver 5.5 or later!" mov ebx, eax ; настраиваем свойства объекта call IRegExp2 ebx METHOD SetGlobal USES ds:eax, ebx, -1 call IRegExp2 ebx METHOD SetIgnoreCase USES ds:eax, ebx, -1 call IRegExp2 ebx METHOD SetMultiline USES ds:eax, ebx, -1 ; задаем шаблон поиска call ANSItoBSTR, offset szPatNum call IRegExp2 ebx METHOD SetPattern USES ds:edx, ebx, eax ; подготавливаем строку для поиска call ANSItoBSTR, offset szTest1 push eax ; сохраняем указатель на BSTR, для SysFreeString ; проверяем строку на соответствие шаблону push eax ; выделяем временный буфер для результата поиска call IRegExp2 ebx METHOD Test USES ds:edx, ebx, eax, esp pop eax ; ax - булевое значение, результат поиска соответствия movsx eax, ax ; расширяем слово до двойного слова со знаком _printf <"Source string: %s",13,10,"Result: %i",13,10>, offset szTest1, eax call SysFreeString ; удаляем BSTR строку ; подготавливаем строку для поиска call ANSItoBSTR, offset szTest2 push eax ; сохраняем указатель на BSTR, для SysFreeString ; проверяем строку на соответствие шаблону push eax ; выделяем временный буфер для результата поиска call IRegExp2 ebx METHOD Test USES ds:edx, ebx, eax, esp pop eax ; ax - булевое значение, результат поиска соответствия movsx eax, ax ; расширяем слово до двойного слова со знаком _printf <"Source string: %s",13,10,"Result: %i",13,10>, offset szTest2, eax call SysFreeString ; удаляем BSTR строку ; уничтожаем строку шаблона push eax ; выделяем временный буфер под указатель на строку шаблона call IRegExp2 ebx METHOD GetPattern USES ds:eax, ebx, esp call SysFreeString ; освобождаем память ; уничтожаем объект и завершаем COM в текущем потоке call DestroyObject, ebx ; ждем нажатия любой клавиши и завершаем программу call _getch call ExitProcess, 0 end Begin Source string: +12E-2 Result: -1 Source string: 02+E-a Result: 0 Метод Replace. Теперь можно попробовать и замену. Если требуемая замена несложная, то лучше всего использовать метод Replace, специально для этого и предназначенный. Он принимает три параметра: первым является указатель на BSTR-строку, в которой и будет производиться поиск соответствий шаблону, вторым параметром является переменная типа VARIANT, в которой описываются условия замены, ну а третьим параметром будет указатель на область памяти, куда метод вернет указатель на результатирующую BSTR-строку. Шаблон поиска соответствий указывается в свойстве Pattern. Казалось бы, все легко и просто, но во втором параметре есть засада Для начала смотрим определение типа VARIANT: struc VARIANT vt dw ? wReserved1 dw ? wReserved2 dw ? wReserved2 dw ? datas dd ? dd ? ends В качестве примера рассмотрим замену сишных объявлений констант на ассемблерные, хотя и в ограниченном варианте: dataseg ; исходный текст szCode db '#define RIGHT_ALT_PRESSED 0x0001', 13, 10 db '#define LEFT_ALT_PRESSED 0x0002', 13, 10 db '#define RIGHT_CTRL_PRESSED 0x0004', 13, 10 db '#define LEFT_CTRL_PRESSED 0x0008', 13, 10 db '#define NLS_ALPHANUMERIC 0x00000000', 13, 10 db '#define NLS_KATAKANA 0x00020000', 13, 10 db 0 ; шаблон для поиска szPattern db '^(#define)(\s+)(\w+\s+)(0x)([a-fA-F0-9]{1,8})', 0 ; шаблон замены szReplace db '$3equ 0$5h', 0 codeseg ; ......... ; задаем шаблон поиска call ANSItoBSTR, offset szPattern call IRegExp2 ebx METHOD SetPattern USES ds:edx, ebx, eax ; подготавливаем строку для поиска call ANSItoBSTR, offset szCode mov esi, eax ; подготавливаем строку для замены найденных соответствий call ANSItoBSTR, offset szReplace push eax ; сохраняем адрес для SysFreeString ; вызываем метод Replace push eax ; выделяем место под результат call IRegExp2 ebx METHOD Replace USES ds:edx, ebx, esi, 8, 0, eax, 0, esp call BSTRtoANSI ; переводим в ANSI push eax ; сохраняем адрес для GlobalFree ; вводим на экран исходный текст и результат замены _printf <"Source:",13,10,"%s",13,10,"Dest:",13,10,"%s",13,10>, offset szCode, eax call GlobalFree ; освобождаем ANSI-строку call SysFreeString ; освобождаем BSTR-строку szReplace call SysFreeString, esi ; освобождаем BSTR-строку szCode Source: #define RIGHT_ALT_PRESSED 0x0001 #define LEFT_ALT_PRESSED 0x0002 #define RIGHT_CTRL_PRESSED 0x0004 #define LEFT_CTRL_PRESSED 0x0008 #define NLS_ALPHANUMERIC 0x00000000 #define NLS_KATAKANA 0x00020000 Dest: RIGHT_ALT_PRESSED equ 00001h LEFT_ALT_PRESSED equ 00002h RIGHT_CTRL_PRESSED equ 00004h LEFT_CTRL_PRESSED equ 00008h NLS_ALPHANUMERIC equ 000000000h NLS_KATAKANA equ 000020000h Метод Execute. Этот метод предназначен для поиска, с анализом и последующей обработкой найденных соответствий. Методу передаются два параметра. Указатель на BSTR-строку, в которой будет производиться поиск и указатель на переменную, под возвращаемое значение, которое является ни чем иным, как ссылкой на объект IMatchCollection, в котором и хранятся все найденные соответствия. Интерфейс IMatchCollection несложен: struc IMatchCollection IDispatch METHOD { virtual Item ; [in] long index, [out, retval] IDispatch** ppMatch virtual Count ; [out, retval] long* pCount virtual NewEnum ; [out, retval] IUnknown** ppEnum } ends ; version 1.0 struc IMatch IDispatch METHOD { virtual Value ; [out, retval] BSTR* pValue virtual FirstIndex ; [out, retval] long* pFirstIndex virtual Length ; [out, retval] long* pLength } ends ; version 5.5 struc IMatch2 IDispatch METHOD { virtual Value ; [out, retval] BSTR* pValue virtual FirstIndex ; [out, retval] long* pFirstIndex virtual Length ; [out, retval] long* pLength virtual SubMatches ; [out, retval] IDispatch** ppSubMatches } ends struc ISubMatches IDispatch METHOD { virtual Item ; [in] long index, [out, retval] VARIANT* pSubMatch virtual Count ; [out, retval] long* pCount virtual NewEnum ; [out, retval] IUnknown** ppEnum } ends Выглядит все это немного запутано, поэтому покажу на примере. Допустим, имеется полный путь к некоторому файлу. С помощью метода Execute очень легко разделить строку на три компоненты: диск, путь и имя файла. А уж затем можно делать с ними, что душе угодно. Итак, некие пути к файлам: dataseg szFiles db '"E:\Program Files\Tasm\Project\RegExpr\exec1.asm"', 13, 10 db '\\Flash0\GdtDump\GdtDump.exe', 13, 10 ; шаблон для поиска szPattern db '(\b[a-z]:|\\\\[a-z0-9]+)\\([^/:*?";;;<>|\r\n]*\\)?([^\\/:*?"<>|\r\n]*)', 0 Теперь вызываем метод Execute: ; настраиваем свойства объекта call IRegExp2 ebx METHOD SetGlobal USES ds:eax, ebx, -1 call IRegExp2 ebx METHOD SetIgnoreCase USES ds:eax, ebx, -1 call IRegExp2 ebx METHOD SetMultiline USES ds:eax, ebx, -1 ; задаем шаблон поиска call ANSItoBSTR, offset szPattern call IRegExp2 ebx METHOD SetPattern USES ds:edx, ebx, eax ; подготавливаем строку для поиска call ANSItoBSTR, offset szFiles push eax ; сохраняем указатель для SysFreeString ; вызываем метод Replace push eax ; выделяем место под результат call IRegExp2 ebx METHOD IRegExp2:Execute USES ds:edx, ebx, eax, esp call ShowMatch call SysFreeString ; освобождаем BSTR-строку szFiles ; уничтожаем строку шаблона push eax ; выделяем временный буфер под указатель на строку шаблона call IRegExp2 ebx METHOD GetPattern USES ds:eax, ebx, esp call SysFreeString ; освобождаем память proc ShowMatch uses esi edi ebx, @@lpMatchCollection local @@count, @@index, @@variant:dword:4 local @@firstidx, @@length mov esi, [@@lpMatchCollection] ; узнаем количество объектов IMatch в коллекции push eax ; выделяем место под результат call IMatchCollection esi METHOD Count USES ds:edx, esi, esp pop ecx ; ecx - количество найденных совпадений or ecx, ecx jz @@nofound ; <- ничего не нашли, уходим mov [@@count], ecx mov [@@index], 0 ; индекс текущего объекта IMatch2 ; цикл по всем найденным соответствиям @@loop: ; получаем ссылку на очередной объект IMatch push eax ; выделяем место под результат call IMatchCollection esi METHOD Item USES ds:eax, esi, [@@index], esp mov edi, [esp] ; edi - экземпляр объекта IMatch ; узнаем значения полей FirstIndex и Length lea edx, [@@firstidx] call IMatch edi METHOD FirstIndex USES ds:eax, edi, edx lea edx, [@@length] call IMatch edi METHOD Length USES ds:eax, edi, edx ; получаем ссылку на строку соответствия call IMatch edi METHOD Value USES ds:eax, edi, esp call BSTRtoANSI ; выводим на экран данные о найденном соответствии _printf <"Match[%i]: %s",13,10," First index: %i",13,10," Length: %i",13,10>, [@@index],eax,[@@fidx],[@@len] ; теперь можно получить субсоответствия, для чего получаем ссылку на ISubMatches ; для версии 1.0 этот код нужно закомментировать, до метки @@next! push eax ; выделяем место под результат call IMatch2 edi METHOD SubMatches USES ds:eax, edi, esp mov edi, [esp] ; edi - экземпляр объекта ISubMatches call ISubMatches edi METHOD Count USES ds:eax, edi, esp pop ebx ; ebx - количество элементов в ISubMatches ; цикл по всем субсоответствиям очередного объекта IMatch @@subloop: dec ebx js @@next lea eax, [@@variant] call ISubMatches edi METHOD Item USES ds:edx, edi, ebx, eax call BSTRtoANSI, [dword @@variant+2*4] _printf <" Sub matches [%i]: %s",13,10>, ebx, eax jmp @@subloop @@next: ; переход к следующему IMatch inc [@@index] dec [@@count] jnz @@loop _printf '-------------------' ret @@nofound: _printf 'no matches found!' ret endp Match[0]: E:\Program Files\Tasm\Project\RegExpr\exec1.asm First index: 1 Length: 47 Sub matches [2]: exec1.asm Sub matches [1]: Program Files\Tasm\Project\RegExpr\ Sub matches [0]: E: Match[1]: \\Flash0\GdtDump\GdtDump.exe First index: 51 Length: 28 Sub matches [2]: GdtDump.exe Sub matches [1]: GdtDump\ Sub matches [0]: \\Flash0 Заключение. Все необходимые сорсы, инклюды и либы в аттаче. Для компиляции всех примеров достаточно запустить makeall.bat, предварительно поместив в папку компилятор tasm32 и линковщик tlink32, или аналогичные, подправив батник. Прикреплённый файлRegExpr.rar (141.7 Кбайт, скачиваний: 395) |