Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Во всех языках высокого уровня среди функций работы со строками присутствуют функции замены заданной подстроки в строке. В Ассемблере такой функции нет, как нет ее и среди функций стандартных библиотек. Замена подстроки на строку такой же длины обычно сложностей не составляет, так как ее можно выполнить прямо в исходной строке без выделения дополнительной памяти. Замена на строку произвольной длины, в том числе и пустую, будет посложнее. Для этого я написал следующую функцию.
  1. ;-----------------------------------------------------
  2. ; Функция замены подстроки в строке
  3. ;-----------------------------------------------------
  4. ; lpSrc - указатель на исходную строку
  5. ; lpDst - указатель на буфер для полученной строки
  6. ; lpPattern - указатель на заменяемую подстроку
  7. ; lpReplace - указатель на строку для замены
  8. ; dNum - количество замен (0 - заменить все)
  9. ;-----------------------------------------------------
  10. proc    _replace lpSrc:DWORD, lpPattern:DWORD, lpReplace:DWORD,\
  11.                  lpDst:DWORD, dNum:DWORD
  12.  
  13.         pusha
  14.  
  15.         ; Указатель на буфер-приемник
  16.         mov     edx,[lpDst]
  17.  
  18.         ; Счетчик замен
  19.         xor     ebx,ebx
  20.  
  21.         ; Исходная строка не пустая?
  22.         mov     ecx,[lpSrc]
  23.         cmp     byte [ecx],0
  24.         jz      .loc_ret
  25.  
  26.         ; Заменяемая строка не пустая?
  27.         mov     eax,[lpPattern]
  28.         cmp     byte [eax],0
  29.         jz      .loc_copy_all
  30.  
  31. .loc_scan:
  32.         mov     esi,ecx
  33.         mov     edi,[lpPattern]
  34.  
  35.         ; Исходная строка закончилась?
  36.         cmp     byte [esi],0
  37.         je      .loc_end_replace
  38. @@:
  39.         ; Строки совпали с паттерном?
  40.         cmp     byte [edi],0
  41.         je      .loc_move_replace
  42.  
  43.         ; Символ совпадает с
  44.         lodsb
  45.  
  46.         ; Заменять все вхождения?
  47.         cmp     [dNum],0
  48.         je      .loc_skip_counter
  49.  
  50.         ; Уже заменили нужное количество?
  51.         cmp     ebx,[dNum]
  52.         je      .loc_move_one_char
  53. .loc_skip_counter:
  54.         cmp     al,byte [edi]
  55.         jne     .loc_move_one_char
  56.  
  57.         inc     edi
  58.         jmp     @b
  59.  
  60. .loc_move_replace:
  61.         ; Увеличить счетчик замен
  62.         inc     ebx
  63.  
  64.         mov     ecx,esi
  65.  
  66.         ; Записать заменяющую строку
  67.         mov     esi,[lpReplace]
  68.         mov     edi,edx
  69. @@:
  70.         lodsb
  71.         or      al,al
  72.         jz      .loc_scan
  73.         stosb
  74.         inc     edx
  75.         jmp     @b
  76.  
  77. .loc_move_one_char:
  78.         ; Скопировать один символ
  79.         mov     al,byte [ecx]
  80.         mov     byte [edx],al
  81.         inc     edx
  82.         inc     ecx
  83.         jmp     .loc_scan
  84.  
  85. .loc_end_replace:
  86.         ; Записать финальный 0 в строку
  87.         mov     byte [edx],0
  88.  
  89.         jmp     .loc_ret
  90. .loc_copy_all:
  91.         ; Просто скопировать исходную строку
  92.         mov     esi,[lpSrc]
  93.         mov     edi,[lpDst]
  94. @@:
  95.         lodsb
  96.         stosb
  97.         or      al,al
  98.         jnz     @b
  99. .loc_ret:
  100.         popa
  101.         ret
  102. endp
Функция самодостаточная, не использует никаких внешних функций и не требует для своей работы дополнительных переменных в памяти. Параметры: lpSrc - указатель на исходную строку в формате ASCIIZ, lpPattern - указатель на искомую подстроку в формате ASCIIZ, lpReplace - указатель на строку для замены в формате ASCIIZ, может быть пустая строка, lpDst - указатель на буфер-приемник полученной после замены строки, его размер должен быть зарезервирован с учетом замененных строк, dNum - количество замен, которое надо выполнить (0 - заменять все вхождения). Я постарался оптимизировать функцию замены по скорости, но, наверное, можно ее ускорить как-нибудь еще.

Теперь несколько примеров использования. Сперва в сегменте данных определим все необходимое, а именно исходную строку, строки для поиска и замены, а также буфер-приемник результата.
  1. section '.data' data readable writeable
  2. ...       
  3. ; Строка для поиска
  4. szStr1  db      'FASM',0
  5. ; Строка для замены
  6. szStr2  db      'Flat Assembler',0
  7.  
  8. ; Исходная строка
  9. buff1   db      'FASM - Fast and Simply!',13,10
  10.         db      'I Like FASM!',13,10
  11.         db      'FASM the Best!',0
  12.  
  13. ; Буфер-приемник результата
  14. buff2   rb      1000h
В сегменте кода функция будет вызвана дважды, первый раз - для замены только первого вхождения подстроки, второй раз - для замены всех вхождений. Как видите, тоже нет ничего сложного.
  1.         ; Заменить только первое вхождение
  2.         stdcall _replace,buff1,szStr1,szStr2,buff2,1
  3.         ...
  4.         ; Заменить все вхождения
  5.         stdcall _replace,buff1,szStr1,szStr2,buff2,0
В приложении пример программы с использованием этой функции, заменяющей сперва первое вхождение подстроки, а затем все вхождения подстроки в строку. Результаты каждой замены выводятся в виде MessageBox.

Примеры программы с исходным текстом (FASM)Примеры программы с исходным текстом (FASM)

String.Replace.Demo.zip (2,171 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 11543 | Комментариев: 8

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
brute (07.04.2012 в 05:33):
есть функция "заменить всё - ReplaceString", а чтобы сделать определённое количество замен - такого нет: ReplaceString("строка","что найти","на что заменить",["учитывать ли регистр",["с какой позиции начать искать"]). Я код скрипта привёл, чтобы сравнить размер кода, понятность-читаемость и размер бинарника...
ManHunter (06.04.2012 в 07:29):
А на бейсике разве нет штатных функций замены?
brute (06.04.2012 в 06:13):
то же на PB: http://webfile.ru/5898434
ManHunter (29.03.2012 в 17:07):
То получится неплохая оптимизация по размеру и быстродействию :) Спасибо!
zummenix (29.03.2012 в 17:05):
А если переменные dSPos и dDPos заменить на регистры ecx и edx?
Voffka (28.03.2012 в 12:02):
Это я к тому что такая функция на ассемблере есть, а уж на каком это второй вопрос.
ManHunter (28.03.2012 в 11:21):
Я не пишу на MASM, и даже не планирую начинать. Тем более, что в szRep нет счетчика замен.
Voffka (28.03.2012 в 11:20):
В масме такая функа есть szRep называется.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.004 сек.) / Память: 4.5 Mb
Наверх