Во всех языках высокого уровня среди функций работы со строками присутствуют функции замены заданной подстроки в строке. В Ассемблере такой функции нет, как нет ее и среди функций стандартных библиотек. Замена подстроки на строку такой же длины обычно сложностей не составляет, так как ее можно выполнить прямо в исходной строке без выделения дополнительной памяти. Замена на строку произвольной длины, в том числе и пустую, будет посложнее. Для этого я написал следующую функцию.
;-----------------------------------------------------
; Функция замены подстроки в строке
;-----------------------------------------------------
; 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.