Как проверить, находятся ли символы в определенных диапазонах значений ascii? - PullRequest
0 голосов
/ 10 ноября 2019

Как проверить, находится ли символ между 0-9, AZ и az? Я понимаю, что вы можете использовать cmp char, 'A' или cmp char, '0' и т. Д. Однако, если мне нужно проверить три разных диапазона, как мне это сделать?

Если мне нужно проверить, является ли «A» <= C <= «Z», тогда мне придется сначала проверить, меньше ли значение символа A, а затем меньше или равно Z. Но так как 0-9 ниже A, как я могу объяснить это, не испортив логику? То же самое касается Z, так как az выше Z. Публикация с моей логикой у меня пока. Я чувствую себя настолько глупо, что не могу получить простые вещи, но я новичок, и я работал над этим в течение нескольких дней, и теперь мне приходится начинать все сначала, поэтому любая помощь будет принята с благодарностью. </p>

_asm
{
   mov ecx, 127
   mov esi, 0
   mov ebx,LocalBuffer[esi] ;LocalBuffer is a c++ array 

Loop1:
   cmp ebx, 'a'     ;ebx is the 0'th index value of LocalBuffer
   jb notLowercase  ;If character value is below 'a'
   cmp ebx,'z'
   jbe CharCount    ;if it's less than or equal to 'z' 
   cmp ebx,'A'
   jb notUpperCase ;If less than 'A', but then won't this discard 0-9?
   cmp ebx,'Z'
   jb CharCount    ;If it's less than 'Z', but what about greater than Z?
   cmp ebx,'0'
   jb NotDigit     ;If less than '0'
   cmp ebx,'9'
   jb CharCount    ;What if it's greater than 9?


notLowerCase:  
;DO I LOOP BACK TO LOOP1, MOVE ON TO THE NEXT CHARACTER OR SOMETHING ELSE? 

notUpperCase:
;SAME ISSUE AS NotLowerCase

notDigit:
;SAME ISSUE AS LAST 2

CharCount:
;Do something


Ответы [ 2 ]

1 голос
/ 10 ноября 2019

Прежде всего, вы не можете отлаживать ветвление, пока не исправите Как загрузить один байт с адреса в сборке - вы загружаете 4 байта символов и сравниваете все это 32-битное значениепротив 'a' и так далее. Используйте movzx вместо mov ebx, LocalBuffer[esi], потому что это массив char.

Если вы выполняли пошаговый код в отладчике, возможно, вы заметили, что все4 байта ebx не равны нулю. Вот почему ваши cmp / ветки не работают или делают то, что вы ожидаете.


@ zx485 объяснил общий случай цепочки ветвей, через которые вы проходите, пока вы не сможете определенно принять или отклонить ввод.

Но вы также можете упростить это, используя эффективные проверки диапазона, используя прием без знака сравнения. Например, Обратный инжиниринг asm с использованием sub / cmp / setbe обратно в C? Моя попытка компилирования по ветвям показывает, как это работает для только нижнего регистра ASCII.

Еще лучше, ASCII удобно спроектирован так, что диапазоны AZ и az совмещаются с каждымдругой, и не пересекайте границу %32, поэтому вы можете перевести байт в нижний регистр с помощью c |= 0x20 или в верхний регистр с помощью c ^= ~0x20. Тогда у вас есть только один диапазон для проверки буквенных символов.

OR с 20h переводит символы верхнего регистра в нижний регистр и не превращает любые не алфавитные символы в нижний регистр, так что вы можетесделайте это с копией своего регистра.

См. В чем идея ^ = 32, которая преобразует строчные буквы в верхние и наоборот? и особенно Как получить доступ кмассив символов и меняйте строчные буквы на прописные, и наоборот для встроенного asm MSVC, который зацикливается на массиве символов и проверяет алфавитный или нет.

Убедитесь, что вы не уничтожаете свою единственную копиюпотому что вам все еще нужно считать верхний отдельно от нижнего;вы просто создаете временную ветвь. Если вы не хотите избегать неиспользуемых позиций в вашем массиве счетчиков, то, возможно, вы захотите c - 'A' в качестве индекса вашего массива. Но, вероятно, нет, если у вас есть один массив для всех символов и цифр, которые вы хотите посчитать.

Пример

Для структуры цикла у меня символы вне диапазона перепрыгивают через Do Somethingчасть, достигнув условия цикла сравнения / ветвления. Приращение загрузки и индекса происходит на каждой итерации, независимо от загруженного символа.

Обратите внимание, что каждый символ, который не входит ни в один из диапазонов, является нецифровым и не буквой. Не имеет смысла выделять целую ветвь без цифр отдельно от цели ветвей без букв, потому что это не то, что вы понимаете. Тем не менее, у вас может быть разветвленная цифра и буква в разных местах.

_asm
{
   xor  esi, esi   ; i=0

Loop1:                           ; do {
   ; load from the array *inside* the loop.
   movzx ebx, byte ptr LocalBuffer[esi]
   inc   esi                          ; ebp = buf[i++]

 ; check for digits first
   lea   eax, [ebx - '0']
   cmp   al, 9
   jbe   CharCount                    ; if (c-'0' <= 9) goto CharCount
 ; non-digits fall through into checking for alphabetic

   mov   eax, ebx
   or    eax, 20h       ; force to lower-case
   sub   eax, 'a'       ; subtract start of the range
   cmp   al, 'z'-'a'    ; see if it was inside the length of the range (unsigned)
   ja    skipCount
; in the common case (alphabetic characters), fall through into CharCount

CharCount:
; EBX still holds the character value, zero-extended
   add  byte ptr [counts + ebx], 1       ;Do something
    ; or use  [counts + ebx*4] if you have an int array.

skipCount:    ; rejected characters jump here, skipping count increment
   cmp  esi, 127
   jb   Loop1               ; } while(i<127)
}

Вам не нужно тратить второй регистр на другой счетчик цикла (ECX), когда у вас уже есть ESI. В любом случае cmp/jb более эффективен, чем инструкция loop.

Я думаю, что мы можем сохранить одну инструкцию, выполнив сначала вычитание (поэтому мы все равно можем использовать lea для копирования и вычитания), нотогда мы должны очистить бит 0x20 вместо его установки, чтобы мы имели дело с прописными буквами.

;; untested, but I think this is correct, too, using LEA+AND instead of MOV+OR+SUB
   lea   eax, [ebx - 'A']
   and   eax, ~20h        ; clear the lower-case bit
   cmp   al, 'Z'-'A'      ; 25, same as 'z'-'a' of course.
   ja    skipCount

c - 'A' = 0x20 для c='a'. Коды символов после 'Z', но до 'a' дают меньшие результаты, поэтому очистка бита 0x20 не может дать нам ложноположительный результат.


PS: если это та же проблема гистограммы, то вызадавал предыдущие вопросы о том, что вам не нужно фильтровать во время чтения, просто сделайте, чтобы в вашем массиве счетчиков было 256 элементов (для каждого возможного значения uint8_t), а затем зацикливайте только те, которые вы хотите напечатать.

Если вы получали segfaults, используя ebx в качестве индекса, это потому, что вы загрузили 4 байта (большое целое число) вместо расширяющего ноль. Мы уже исправили эту ошибку в предыдущих версиях вашего вопроса.

Также, как я ранее объяснил в комментариях , вам не нужно копировать строковый ввод в LocalBuffer, просто сделайте char *bufptr = Buffer;, а во встроенном ассемблере выполните mov esi, bufptr, чтобы получить этоуказатель в регистр. Это неэффективно, но гораздо лучше, чем копировать весь массив. Особенно для подсчетов.

Или https://godbolt.org/z/QszVMf показывает, как получить доступ к членам класса из встроенного asm.

1 голос
/ 10 ноября 2019

Простой подход - упорядочить диапазоны по возрастанию (или убыванию). Затем вы можете использовать cmp s в стиле ВКЛ / ВЫКЛ:

   mov ecx, 127    ; Check a 127 char string
   mov esi, 0
Loop1:
   movzx ebx, byte ptr LocalBuffer[esi]   ; Load a byte from the address  
   cmp bl, '0'     ; '0' = 48 - all lower values mask are NOT IN THE SET
   jb  notInSet    ; 
   cmp bl,'9'      ; '9' = 57 - all lower are IN THE SET
   jbe CharCount   ; It is a number 
   cmp bl,'A'      ; 'A' = 65 - all lower are NOT IN THE SET
   jb  notInSet    ; If less than 'A'
   cmp bl,'Z'      ; 'Z' = 90 - all lower are IN THE SET
   jbe CharCount   ; It is an uppercase char
   cmp bl,'a'      ; 'a' = 97 - all lower are NOT IN THE SET
   jb  NotInSet    ; 
   cmp bl,'z'      ; 'z' = 122 - all lower are IN THE SET
   jbe CharCount   ; It is a lowercase letter
   ; FALL THROUGH for greater values
notInSet:  
   inc esi
   loop Loop1
   jmp Final

CharCount:
   ; DO SOMETHING (that doesn't mess up ECX, ESI)
   inc esi
   loop Loop1
   ; FALL THROUGH to Final

Final:
   ; END of this snippet

Как видите, проверенные значения возрастают. Например, значение 3 (= 51) сначала проверит, находится ли оно ниже 48 (= НЕТ), а затем проверит, не ниже ли оно 57 (= ДА), поэтому будет выполнен второй скачок.


Альтернативой является использование таблицы переходов с индексированной адресацией. При таком подходе вы определяете диапазоны в виде таблицы логических значений (0 = NotInSet, 1 = CharCount):

Таблица должна быть настроена так в сегменте .data для вашего сценария (обратите внимание на чередованиезначения 0 и 1 (стиль ВКЛ / ВЫКЛ, упомянутый выше):

.data
  JumpTable db 48 dup(0), 10 dup(1), 7 dup(0), 26 dup(1), 7 dup(0), 26 dup(1), 133 dup(0)

Тогда код может выглядеть следующим образом:

   mov ecx, 127
   mov esi, 0
Loop1:
   movzx ebx, byte ptr LocalBuffer[esi]   ; Load a byte from the address  
   movzx eax, byte ptr JumpTable[ebx]     ; Retrieve the ebx'th value of the table[
   test eax, eax    ; Check if it's zero
   jnz  CharCount   ; If it's not, it's a char, so jump to CharCount 
   ; FALL THROUGH TO notInSet
notInSet:  
   inc esi
   loop Loop1
   jmp Final

CharCount:
   ; DO SOMETHING (that doesn't mess up ECX, ESI)
   inc esi
   loop Loop1
   ; FALL THROUGH to Final

Final:
   ; END of this snippet

Таблица имеет 256 значений, полный диапазон ASCII, 0 или 1.

В обоих случаях вы можете переместить inc esi в начало, сразу после того, как значение было прочитано movzx ebx, byte ptr LocalBuffer[esi].

...