Поиск первой и последней заглавной буквы в пользовательском вводе - PullRequest
2 голосов
/ 29 июня 2019

Входные данные должны быть взяты из az или AZ, а ввод завершается звездочкой *.

. В качестве выходных данных мы должны использовать первую и последнюю заглавные буквы входных символов.Кроме того, мы должны показать вклад, который мы взяли каждый раз.NB Мы берем входные данные символьно, а не как строку.

Контрольный пример 1: ввод: aAbCcP* вывод: AP

Контрольный пример 2: ввод: ZabCBc* вывод: ZB

Я написал этот код ниже, который удовлетворяет условию 1, но не 2:

.MODEL
.STACK 100H
.DATA
   STR DB 'Enter letters:$'
.CODE

MAIN PROC

MOV AX, @DATA
MOV DS, AX

LEA DX, STR
MOV AH, 9
INT 21H 

cycle: 

    MOV AH, 1
    INT 21H

    CMP AL, '*'
    JZ output 
    CMP AL, 'Z' 
    JA save


head: 
    CMP BL, 1
    JZ save

    MOV BL, 1
    MOV BH, AL 

clear:
    XOR AL, AL  

save:
    MOV CH, AL

LOOP cycle 

output:
    MOV AH, 2
    MOV DL, BH
    INT 21H 

    MOV AH, 2
    MOV DL, CH
    INT 21H 


MAIN ENDP 
END MAIN 

Ответы [ 2 ]

3 голосов
/ 30 июня 2019

Сначала задайте себе следующие вопросы:

  • Что такое заглавные буквы?
    Если мы не рассматриваем символы с ударением, то заглавные буквы - это символы с кодами ASCIIв диапазоне от 65 до 90.

  • Могу ли я доверять пользователю только ввод символов из аз или аз?
    Нет, вы не можете.Вы не можете контролировать то, что пользователь делает за клавиатурой, и поэтому ваша программа должна использовать защитный подход и тестировать столицы с чем-то лучшим, чем один cmp al, 'Z'.

  • Каким будет результат, если на входе не будет одной заглавной буквы?
    Вы можете распечатать два пробела или описательное сообщение, или как я вообще ничего не отображал.

  • Каков будет результат, если ввод содержит только одну заглавную букву?
    Вы можете распечатать эту заглавную букву, или, как я это сделал, дважды, потому что еслиВы думаете об этом, что единый капитал является одновременно первым вхождением капитала, а также последним вхождением капитала.

  • Какие функции ввода / вывода я будуиспользовать?
    Для односимвольного ввода у вас есть выбор между функциями DOS 01h, 06h, 07h, 08h, 0Ch и 3Fh.
    Для односимвольного вывода у вас есть выбор между функциями DOS 02h, 06h,а также 40h.
    Если вы новичок в сборке, придерживайтесь более простых и используйте функции 01h и 02h.Обратитесь к справочнику API перед использованием любой функции DOS.И, конечно же, проверьте с помощью emu8086, поддерживает ли она эту функцию вообще!

Вам необходимо решить все вышеперечисленное, чтобы решить задачу.Важно то, что за каждый ваш выбор вы можете отстаивать свой выбор.


Ниже приведен мой вариант этой задачи.Для простоты я использую крошечную программную модель.Смотрите директиву ORG 256 сверху?Эта программная модель имеет основное преимущество, заключающееся в том, что все регистры сегментов указывают одинаково на вашу программу (CS = DS = ES = SS).

Программа запускает 2 цикла. Первый цикл выполняется до получения капитала .(Само собой разумеется, что он останавливается раньше, если входные данные содержат звездочку.) Поскольку этот капитал является одновременно первым вхождением капитала, а также последним вхождением капитала, я сохраняю его дважды, как в DL, так и вDH.

Второй цикл работает до тех пор, пока не будет получена звездочка .Каждый раз, когда появляется новая столица, она заменяет то, что написано в DH.Когда этот цикл окончательно заканчивается, на экране отображаются и DL, и DH, и, конечно, в следующем порядке.

Программа завершает работу с предпочтительной функцией DOS 4Ch для завершения программы.

Я написал несколько важных комментариев, воздерживался от добавления избыточных и использовал описательные имена для меток в программе.Обратите внимание, что хороший табличный макет.Для удобства чтения это очень важно.

        ORG     256

Loop1:  mov     ah, 01h     ; DOS.GetKeyboardCharacter
        int     21h         ; -> AL
        cmp     al, "*"     ; Found end of input marker ?
        je      Done
        cmp     al, "A"
        jb      Loop1
        cmp     al, "Z"
        ja      Loop1
        mov     dl, al      ; For now it's the first
        mov     dh, al      ; AND the last capital

Loop2:  mov     ah, 01h     ; DOS.GetKeyboardCharacter
        int     21h         ; -> AL
        cmp     al, "*"     ; Found end of input marker ?
        je      Show
        cmp     al, "A"
        jb      Loop2
        cmp     al, "Z"
        ja      Loop2
        mov     dh, al      ; This is the latest capital
        jmp     Loop2

Show:   mov     ah, 02h     ; DOS.DisplayCharacter
        int     21h         ; -> (AL)
        mov     dl, dh
        mov     ah, 02h     ; DOS.DisplayCharacter
        int     21h         ; -> (AL)

Done:   mov     ax, 4C00h   ; DOS.TerminateWithReturnCode
        int     21h

Пример:

a Z e R T y *

aZeRTy * ZT


Это было бы очень разочаровывающимесли вы выбрали простой способ и просто скопируйте / вставьте мой кодЯ попытался объяснить это очень подробно и надеюсь, что вы многому научитесь из этого.

Мое решение, конечно, не единственное хорошее решение для этой задачи.Например, вы можете сначала ввести все символы и сохранить их где-нибудь в памяти, после чего вы будете обрабатывать эти символы из памяти, как я это сделал.
Пожалуйста, попробуйте написать рабочую версию, которая делает это альтернативным способом.может только стать умнее!Удачного программирования.

1 голос
/ 30 июня 2019

Ваш код поврежден, потому что вы всегда переходите на save: MOV CH, AL каждую итерацию, поэтому он может работать, только если последняя заглавная буква также является последним символом всего ввода.

Один шаг с отладчиком для простого ввода, например ABc*, чтобы увидеть, как все идет не так.

Также вы используете loop, что похоже на dec cx/jnz.Это не имеет смысла, потому что нет условия завершения на основе счетчика, и может потенциально повредить CH, если CL был нулевым.Вы даже не инициализируете CX в первую очередь!Инструкция loop - не единственный способ зацикливания;это просто оптимизация глазка размера кода, которую вы можете использовать, когда удобно использовать CX в качестве счетчика цикла.В противном случае не используйте его.


Это упрощенная версия реализации Sep, в которой используется тот факт, что ввод гарантированно будет буквенным, поэтому мы действительно можем проверить верхний регистр так же легко, какc <= 'Z' (после исключения терминатора '*').Нам не нужно беспокоиться о входных данных, таких как 12ABcd7_ или пробелах или символах новой строки, которые также имеют более низкие коды ASCII, чем алфавитный диапазон верхнего регистра.Ваша проверка cmp al,'Z' / ja была правильной, просто у кода, к которому вы переходили, не было здравой логики.

Даже если вы действительно хотели строго проверить c >= 'A' && c <= 'Z', эта проверка диапазона можетбыть сделано с одной веткой, используя sub al,'A';cmp al,'Z'-'A';ja non_upper вместо пары веток cmp / jcc.(Это изменяет оригинал, но если вы сохраните его в SI или что-то еще, вы можете позже восстановить его с помощью lea ax, [si+'A'])

Вы также можете поместить условную ветвь внизу цикла для обоих циклов вместоjmp внизу и if() break внутри.Код Sep уже сделал это для первого цикла.

Я согласен с Sep, что иметь 2 цикла проще, чем проверять флаг каждый раз, когда вы находите заглавную букву (чтобы увидеть, является ли она первой заглавной или нет).

        ORG     100h        ; DOS .com is loaded with IP=100h, with CS=DS=ES=SS
                            ; we don't actually do any absolute addressing so no real effect.

        mov     ah, 01h     ; DOS.GetKeyboardCharacter
                            ; AH=01 / int 21h doesn't modify AH so we only need this once
find_first_cap:  
        int     21h         ; stdin -> AL
        cmp     al, '*'     ; Found end of input marker ?
        je      Done        ;  if (c=='*') return;  without print anything, we haven't found a capital yet

        cmp     al, 'Z'
        ja      find_first_cap
    ; fall through: AL <= 'Z' and we can assume it's a capital letter, not a digit or something.

        mov     dl, al      ; For now it's the first
        ;mov     dh, al      ; AND the last capital

        ;mov     ah, 01h     ; DOS.GetKeyboardCharacter   AH still = 01
        ;jmp     loop2_entry      ; we can let the first iteration set DH
Loop2:                      ; do {
        cmp     al, 'Z'       ; assume all c <= 'Z' is a capital alphabetic character
        ja      loop2_entry
        mov     dh, al        ; This is the latest capital

loop2_entry:
        int     21h         ; stdin -> AL
        cmp     al, '*'
        jne     Loop2       ; }while(c != '*');


Show:   mov     ah, 02h     ; DOS.DisplayCharacter
        int     21h         ; AL -> stdout
        mov     dl, dh
        ; mov     ah, 02h     ; DOS.DisplayCharacter
        int     21h         ; AL -> stdout

Done:   mov     ax, 4C00h   ; DOS.TerminateWithReturnCode
        int     21h

На данный момент это, возможно, не проще, но более оптимизировано, особенно для размера кода.Это имеет место, когда я пишу что-нибудь, потому что это забавная часть.: P

Наличие взятой ветви внутри цикла для случая, не являющегося заглавной, возможно, ухудшает производительность.(В современном коде для P6-совместимого процессора вы, вероятно, использовали бы cmovbe esi, eax вместо условного перехода, потому что условный ход равен точно , что вы хотите.)

Пропуск mov ah, XX до int 21h, потому что он все еще установлен, не делает вашу программу более удобочитаемой, но это безопасно, если вы тщательно проверяете документы для каждого вызова, чтобы убедиться, что они неничего не вернуть в АХ.

...