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

Последнее обновление: 25.09.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 в этом случае хранит количество элементов, а не байтов, которые необходимо обработать при сканировании.

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

.globl _start

.data
str: .asciz "test"
message: .asciz "Hello"     # исходная строка
len = . - message           # длина строки 
char: .byte 'l'             # символ для поиска

.text
_start:
    movq $message, %rdi # загружаем адрес переменной message
    movb char, %al      # в AL символ для поиска
    movq $len, %RCX     # в RCX количество символов строки
    repne scasb         # ищем байт
    jz found            # если символ найден, устанавливается флаг нуля ZF
    movq $-1, %rdi      # если символ не найден, в RDI число -1
    jmp exit
found:              
    subq $message, %rdi     # если символ найден, получаем индекс следующего символа после найденного
    decq %rdi           # отнимает 1 байт - получем индекс найденного символа
exit:
    movq $60, %rax
    syscall

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

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

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

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

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

.globl _start

.data
message: .asciz "Hello METANIT.COM"     # исходная строка

.text
_start:
    movq $message, %rdi # загружаем адрес переменной message
    movq %rdi, %rsi     # копируем адрес в RSI для последующего поиска индекса
    movb $0, %al        # в AL символ для поиска
    movq $-1, %rcx      # максимальное число
    repne scasb         # ищем байт
    subq $message, %rdi     # если символ найден, получаем индекс следующего символа после найденного
    decq %rdi           # отнимает 1 байт - получем индекс найденного символа
exit:
    movq $60, %rax
    syscall

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

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