Поиск в строке

Последнее обновление: 16.10.2023

Для поиска определенного элемента в строке применяется следующий набор инструкций:

  • scasb: поиск в строке байтов

  • scasw: поиск в строке слов

  • scasd: поиск в строке двойных слов

  • scasq: поиск в строке четверных слов

Данные инструкции принимают только один операнд - строку, в которую помещается результат поиска и адрес которой загружается в регистр RDI. Строка для поиска загружается в регистре AL (scasb), AX (scasw), EAX (scasd) или RAX (scasq). Инструкции scas сравнивают значение в регистре AL/AX/EAX/RAX со значением, на которое указывает RDI, а затем увеличивает (или уменьшает) адрес в RDI на 1, 2, 4 или 8. В соответствии с результатом сравнения процессор устанавливает флаги. Так, если символ найден, то устанавливается флаг нуля, а регистр RDI указывает на символ, следующий за найденным.

При применении префикса repe инструкции scas просматривают строку в поисках элемента, не соответствующего значению в аккумуляторе. При использовании префикса repne scas сканирует строку в поисках первого элемента, равного значению в аккумуляторе. Регистр RCX в этом случае хранит количество элементов, а не байтов, которые необходимо обработать при сканировании.

Например, найдем индекс символа в строке:

global _start

section .data
message db "Hello"     ; исходная строка
len equ $-message      ; длина строки 
char db "l"            ; символ для поиска

section .text
_start:
    mov rdi, message    ; загружаем адрес переменной message
    mov al, [char]        ; в AL символ для поиска
    mov rcx, len        ; в RCX количество символов строки
    repne scasb         ; ищем байт
    jz found            ; если символ найден, устанавливается флаг нуля ZF
    mov rdi, -1        ; если символ не найден, в RDI число -1
    jmp exit
found:              
    sub rdi, message     ; если символ найден, получаем индекс следующего символа после найденного
    dec rdi           ; отнимает 1 байт - получем индекс найденного символа
exit:
    mov rax, 60
    syscall 

Здесь в строке "Hello" ищем символ "l". Для этого строку для поиска загружаем в RDI, а в AL - символ (байт), который будем искать.

Если при выполнении инструкции scas элемент был найден, то устанавливается флаг нуля. И мы можем это проверить и выполнить определенные действия. В данном случае, если символ найден, от значения в RDI (адрес следующего символа за найденным) отнимаем начальный адрес строки message (адрес первого символа строки message) и таким образом получаем расстояние в байтах между первым символом и следующим за найденным символом.

Поскольку все элементы строки - байты, то это расстояние будет представлять индекс следующего символа за найденным. Соответственно, чтобы получить индекс найденного символа (а не следующего за ним), надо от этого расстояния отнять 1 (размер одного элемента в байтах). Например, при поиске символа "l" в строке "Hello" мы получим 2 - это индекс третьего символа.

Если символ не найден, то в регистр RDI помещаем -1.

Инструкция scasb очень удобна, если нам надо найти длину строку, которая завершется каким-нибудь концевым символом, например, нулевым байтом. К примеру:

global _start

section .data
message db "Hello METANIT.COM", 0     ; исходная строка с концевым нулевым байтом

section .text
_start:
    mov rdi, message ; загружаем адрес переменной message
    mov rsi, rdi     ; копируем адрес в RSI для последующего поиска индекса
    mov al, 0        ; в AL символ для поиска
    mov rcx, -1      ; максимальное число
    repne scasb         ; ищем байт
    sub rdi, message     ; если символ найден, получаем индекс следующего символа после найденного
    dec rdi           ; отнимает 1 байт - получем индекс найденного символа - длину строки
    mov rax, 60
    syscall 

В данном случае предполагаем, что наша строка завершается нулевым байтом, поэтому ищем в ней индекс нулевого байта. Основная сложность здесь заключается в том, что строки могут быть разные по длине, соответственно мы не знаем точное количество символов. Поэтому в регистр RCX помещаем очень большое положительное число, которое соответствует отрицательному числу -1 в дополнительном коде:

mov rcx, -1      ; максимальное число
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850