输入将从a-z或A-Z中获取,并且输入以星号stg.customers_testcontrol_staging
结尾。
我们需要输入字符的第一个和最后一个大写字母作为输出。另外,我们应该显示每次使用的输入。 N.B.我们按字符而不是字符串来接受输入。
测试案例1:输入:*
输出:aAbCcP*
测试案例2:输入:AP
输出:ZabCBc*
我在下面编写了这段代码,它满足测试用例1,但不满足2:
ZB
答案 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
中写的内容。当此循环最终结束时,DH
和DL
都按照顺序显示在屏幕上。
程序以首选的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返回任何内容。