Замена подстроки в строке на Ассемблере

Во всех языках высокого уровня среди функций работы со строками присутствуют функции замены заданной подстроки в строке. В Ассемблере такой функции нет, как нет ее и среди функций стандартных библиотек. Замена подстроки на строку такой же длины обычно сложностей не составляет, так как ее можно выполнить прямо в исходной строке без выделения дополнительной памяти. Замена на строку произвольной длины, в том числе и пустую, будет посложнее. Для этого я написал следующую функцию.
;-----------------------------------------------------
; Функция замены подстроки в строке
;-----------------------------------------------------
; lpSrc - указатель на исходную строку
; lpDst - указатель на буфер для полученной строки
; lpPattern - указатель на заменяемую подстроку
; lpReplace - указатель на строку для замены
; dNum - количество замен (0 - заменить все)
;-----------------------------------------------------
proc    _replace lpSrc:DWORD, lpPattern:DWORD, lpReplace:DWORD,\
                 lpDst:DWORD, dNum:DWORD
 
        pusha
 
        ; Указатель на буфер-приемник
        mov     edx,[lpDst]
 
        ; Счетчик замен
        xor     ebx,ebx
 
        ; Исходная строка не пустая?
        mov     ecx,[lpSrc]
        cmp     byte [ecx],0
        jz      .loc_ret
 
        ; Заменяемая строка не пустая?
        mov     eax,[lpPattern]
        cmp     byte [eax],0
        jz      .loc_copy_all
 
.loc_scan:
        mov     esi,ecx
        mov     edi,[lpPattern]
 
        ; Исходная строка закончилась?
        cmp     byte [esi],0
        je      .loc_end_replace
@@:
        ; Строки совпали с паттерном?
        cmp     byte [edi],0
        je      .loc_move_replace
 
        ; Символ совпадает с
        lodsb
 
        ; Заменять все вхождения?
        cmp     [dNum],0
        je      .loc_skip_counter
 
        ; Уже заменили нужное количество?
        cmp     ebx,[dNum]
        je      .loc_move_one_char
.loc_skip_counter:
        cmp     al,byte [edi]
        jne     .loc_move_one_char
 
        inc     edi
        jmp     @b
 
.loc_move_replace:
        ; Увеличить счетчик замен
        inc     ebx
 
        mov     ecx,esi
 
        ; Записать заменяющую строку
        mov     esi,[lpReplace]
        mov     edi,edx
@@:
        lodsb
        or      al,al
        jz      .loc_scan
        stosb
        inc     edx
        jmp     @b
 
.loc_move_one_char:
        ; Скопировать один символ
        mov     al,byte [ecx]
        mov     byte [edx],al
        inc     edx
        inc     ecx
        jmp     .loc_scan
 
.loc_end_replace:
        ; Записать финальный 0 в строку
        mov     byte [edx],0
 
        jmp     .loc_ret
.loc_copy_all:
        ; Просто скопировать исходную строку
        mov     esi,[lpSrc]
        mov     edi,[lpDst]
@@:
        lodsb
        stosb
        or      al,al
        jnz     @b
.loc_ret:
        popa
        ret
endp

Функция самодостаточная, не использует никаких внешних функций и не требует для своей работы дополнительных переменных в памяти. Параметры: lpSrc - указатель на исходную строку в формате ASCIIZ, lpPattern - указатель на искомую подстроку в формате ASCIIZ, lpReplace - указатель на строку для замены в формате ASCIIZ, может быть пустая строка, lpDst - указатель на буфер-приемник полученной после замены строки, его размер должен быть зарезервирован с учетом замененных строк, dNum - количество замен, которое надо выполнить (0 - заменять все вхождения). Я постарался оптимизировать функцию замены по скорости, но, наверное, можно ее ускорить как-нибудь еще.

Теперь несколько примеров использования. Сперва в сегменте данных определим все необходимое, а именно исходную строку, строки для поиска и замены, а также буфер-приемник результата.
section '.data' data readable writeable
...       
; Строка для поиска
szStr1  db      'FASM',0
; Строка для замены
szStr2  db      'Flat Assembler',0
 
; Исходная строка
buff1   db      'FASM - Fast and Simply!',13,10
        db      'I Like FASM!',13,10
        db      'FASM the Best!',0
 
; Буфер-приемник результата
buff2   rb      1000h

В сегменте кода функция будет вызвана дважды, первый раз - для замены только первого вхождения подстроки, второй раз - для замены всех вхождений. Как видите, тоже нет ничего сложного.
        ; Заменить только первое вхождение
        stdcall _replace,buff1,szStr1,szStr2,buff2,1
        ...
        ; Заменить все вхождения
        stdcall _replace,buff1,szStr1,szStr2,buff2,0

В приложении пример программы с использованием этой функции, заменяющей сперва первое вхождение подстроки, а затем все вхождения подстроки в строку. Результаты каждой замены выводятся в виде MessageBox.
Теги:
строка
Добавлено: 28 Сентября 2013 10:08:43 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...