在用户输入中查找第一个和最后一个大写字母

时间:2019-06-29 17:42:12

标签: assembly emu8086

输入将从a-z或A-Z中获取,并且输入以星号stg.customers_testcontrol_staging结尾。

我们需要输入字符的第一个和最后一个大写字母作为输出。另外,我们应该显示每次使用的输入。 N.B.我们按字符而不是字符串来接受输入。

测试案例1:输入:*输出:aAbCcP*

测试案例2:输入:AP输出:ZabCBc*

我在下面编写了这段代码,它满足测试用例1,但不满足2:

ZB

2 个答案:

答案 0 :(得分:3)

首先问自己以下问题:

  • 什么是大写字母?
    如果不考虑带重音符号的字符,则大写字母是指ASCII码在65到90之间的字符。

  • 我可以信任用户仅输入a-z或A-Z中的字符吗?
    不,你不能。您无法控制用户在键盘上的操作,这就是为什么您的程序应该采取防御性方法并以比单个cmp al, 'Z'更好的方式测试资本。

  • 如果输入不包含单个大写字母,结果将是什么?
    您可以选择打印两个空格或描述性消息,或者就像我什么都没显示一样。

  • 如果输入仅包含一个大写字母,结果将是什么?
    您可以选择打印一个大写字母,或者像我确实显示两次一样,因为考虑到它,那个单个大写字母同时是首个出现的资本,也是最后一个出现的资本。

  • 我将使用哪些输入/输出功能?
    对于单字符输入,可以在DOS功能01h,06h,07h,08h,0Ch和3Fh之间进行选择。
    对于单字符输出,可以在DOS函数02h,06h和40h之间进行选择。
    如果您不熟悉汇编,则坚持使用简单的汇编,并使用功能01h和02h。使用任何DOS功能之前,请先咨询API reference。当然,请使用emu8086检查它是否完全支持该功能!

您需要对以上所有内容进行决策才能解决此任务。重要的是,对于您做出的每个选择,您都可以捍卫自己的选择。


下面是此任务的我版本。为简单起见,我使用的是微型程序模型。看到顶部的ORG 256指令?此程序模型的主要优点是,所有段寄存器均均指向您的程序(CS = DS = ES = SS)。

程序运行2个循环。 第一个循环一直进行到收到大写为止。 (不用说,如果输入中包含星号,它将更早停止。)因为该资本同时是资本的首次出现和资本的最后出现,所以我将其保存两次,两次都保存在{{1}中}和DL

第二个循环运行,直到收到星号。每次出现新的大写字母时,它都会替换DH中写的内容。当此循环最终结束时,DHDL都按照顺序显示在屏幕上。

程序以首选的DOS功能4Ch退出,以终止程序。

我写了一些基本的注释,避免添加多余的注释,并为程序中的标签使用了描述性名称。请注意漂亮的表格布局。为了提高可读性,这很关键。

DH

示例:

a Z e R T y *

  

aZeRTy * ZT


如果您采用简单的方法并仅复制/粘贴我的代码,那将非常令人失望。我试图详细解释它,希望您能从中学到很多。

对于这个任务,我的解决方案当然不是唯一好的解决方案。您可以例如首先输入所有字符并将它们存储在内存中的某个位置,然后按照与我的操作类似的方式从内存中处理这些字符。
请尝试编写可通过这种替代方式完成工作的版本。您只会变得更聪明!编程愉快。

答案 1 :(得分:1)

您的代码已损坏,因为您每次迭代总是 失败,所以只有在最后一个大写字母也是整个输入的最后一个字符时,它才起作用。

使用调试器对它进行单步操作,以输入诸如save: MOV CH, AL之类的简单输入,以查看错误的原因。

此外,您使用ABc*,就像loop。这没有任何意义,因为没有基于计数器的终止条件,并且如果CL为零,则可能破坏CH。您甚至不需要先初始化CX! dec cx/jnz指令不是唯一的循环方式;这只是代码大小的窥孔优化,可以在方便使用CX作为循环计数器时使用。否则不要使用它。


这是Sep的实现的简化版本,它利用了保证输入为字母的事实,因此我们真的可以像检查loop一样轻松地检查大写字母(排除了{{1 }}终止符)。我们不必担心c <= 'Z',空格或换行符之类的输入,它们的ASCII码也比大写字母范围小。您的'*' / 12ABcd7_检查是正确的,只是您分支到的代码没有合理的逻辑。

即使您确实想严格检查cmp al,'Z',也可以使用ja在一个分支上完成范围检查; c >= 'A' && c <= 'Z'; sub al,'A',而不是一对cmp / jcc分支。 (这会修改原始文件,但是如果您将其保存在SI中或以后可以通过cmp al,'Z'-'A'进行恢复)

对于两个循环,您还可以在循环的底部放置条件分支,而不是在底部放置ja non_upper并在内部放置lea ax, [si+'A']。 Sep的代码已经在第一个循环中做到了。

我同意Sep的观点,即每次找到一个大写字母(看看它是否是第一个大写字母)时,具有2个循环比检查一个标志更容易。

jmp

在这一点上,它可以说不是更简单,但是尤其是对于代码大小,它已经进行了优化。当我写任何东西时,这往往会发生,因为那是有趣的部分。 :P

对于非大写字母的情况,在循环内具有分支分支可能会降低性能。 (在与P6兼容的CPU的现代代码中,您可能会使用if() break而不是条件分支,因为条件移动恰恰是您想要的 。)

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 之前省略cmovbe esi, eax,因为它仍然被设置不会使您的程序更具可读性,但是如果您仔细检查每个the docs是安全的致电以确保他们没有用AH返回任何内容。

相关问题