如何显示汇编中两个字符串之间相同和不同字符的数量?
How do I display the number of equal and different characters between two strings in assembly?
程序将要求 2 个字符串(例如,最大长度为 150 个字符)。
输入
字符串 1: "Hello this is the first string"
字符串 2: "Woops this string is different and longer!"
输出
Number of equal characters = 26
Number of different characters = 15
我记得我们不必“计算”不同的字符,因为我们可以获得最长字符串的大小并减去相等字符的数量,但我不知道如何先做这个比较(相等字符的个数)。
我该怎么做?我可以为那个比较做一个宏吗?
更新
我用的是EMU8086,MASM。我不想使用字符串指令。
这是我当前的代码,按照评论中的说明进行操作,但我仍然无法正常工作。
.model small
.stack 100h
.data
Text1 DB "Please enter the first phrase: ",13,10,'$'
Text2 DB "Please enter the second phrase: ",13,10,'$'
max1 DB 151 ;We add one to 150 for the ENTER
charRead1 DB 0
max2 DB 151 ;We add one to 150 for the ENTER
charRead2 DB 0
TextoEquals DB "Number of equal characters: ",13,10,'$'
TextoDiffer DB "Number of different characters: ",13,10,'$'
linefeed DB 13, 10, "$"
size1 dw 0000h
size2 dw 0000h
.code
AllMacros:
MagicFunction MACRO
cld ; Clear direction flag
xor bx, bx ; Reset counter
mov si, OFFSET max1 ; Pointer to shorter string
mov dx, size1 ; Length of the shorter string
NextChar:
lodsb ; Fetch next character from shorter string
mov di, offset max2 ; Pointer to longer string
mov cx, size2 ; Length of the longer string
repne
scasb
jne NotFound
inc bx ; Increment counter
mov byte ptr [di-1], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
ENDM
PrintText MACRO str
MOV AH, 09h
LEA DX, str
INT 21h
ENDM
PrintCls MACRO
mov ah, 09h
mov dx, offset linefeed
int 21h
ENDM
Inicio:
mov ax, @data
mov ds, ax
PrintText Text1
mov ah, 0Ah
lea dx, max1
mov size1, offset max1
int 21h
PrintCls
PrintText Text2
mov ah, 0Ah
lea dx, max2
mov size2, offset max2
int 21h
PrintCls
MagicFunction
PrintText TextoEquals
mov ah, 9
lea dx, bx
int 21h
PrintCls
PrintText TextoDiffer
mov ah, 9
lea dx, dx
int 21h
END Inicio
你会如何在纸上做到这一点?
您将依次考虑较短字符串中的每个字符,并尝试在较长字符串中找到它。如果您找到了该字符,那么您将递增计数器,并从较长的字符串中删除该字符,这样就无法再次找到该字符。
发件人:
-- ----------- --------- ----
Hello this is the first string
Woops this string is different and longer!
- ----------------- -- --- -- -
收件人:
Hello这是the第一个st环(剩余4个字符)
==> 30 - 4 = 26 是 'equal'
Woops 这个字符串是 diff erent and longer!(剩余 16 个字符)
==> 16 个是 'different'
在汇编代码中它可能如下所示:
S1 db "Hello this is the first string"
S2 db "Woops this string is different and longer!"
...
cld ; Clear direction flag
xor bx, bx ; Reset counter
mov si, S1 ; Pointer to shorter string
mov dx, 30 ; Length of the shorter string
NextChar:
lodsb ; Fetch next character from shorter string
mov di, S2 ; Pointer to longer string
mov cx, 42 ; Length of the longer string
repne scasb
jne NotFound
inc bx ; Increment counter
mov byte [di-1], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
scasb
将 AL
与 [es:di]
设置标志处的字节进行比较,并递增 DI
寄存器(前提是方向标志已清除)。
repne scasb
只要 CX
中的计数未用完且未设置零标志,就会一直比较。
I have in mind that we don't have to "calculate" the different characters as we can get the size of the longest string and subtract the number of equal characters...
没错。
上面的代码应该会产生下一个结果:
相等字符数 = 26
不同字符的数量 = 42 - 26 = 16
感谢您在问题中包含代码并适当地标记它。这次我可以写一篇完整的评论。请注意,另一个答案并不像您似乎暗示的那样半个答案。这个答案与你当时的问题是一致的。
How can I do this? Could I do a macro for that comparison?
我在之前的回答中已经提供的代码,并且您已经采用了,并不是真的要用作宏。如果有的话,它可以成为一个子例程,但我认为在您的程序中这样做没有任何好处。只需内联代码。
Inicio:
mov ax, @data
mov ds, ax
正如我在之前的回答中所写,scasb
指令使用了 ES
段寄存器。您应该将其设置为与 DS
段寄存器相同:
Inicio:
mov ax, @data
mov ds, ax
mov es, ax
正在输入
详细解释了 DOS.BufferedInput 函数 0Ah 的工作原理。
你写的地方:
max1 DB 151 ;We add one to 150 for the ENTER
charRead1 DB 0
您忘记为您希望从用户那里收到的实际字符预留空间。接下来是它需要的样子:
max1 DB 151
charRead1 DB 0
chars1 DB 151 dup(0)
当您调用此 DOS 函数 0Ah 时,您还将在 charRead1 字节中收到实际输入的长度。这是您需要存储在 size1 变量中的值(不是像您的代码现在所做的输入结构的地址!):
mov ah, 0Ah
lea dx, max1
mov size1, offset max1 <<<< is wrong
int 21h
当然,您只能在 DOS 调用完成后执行此操作:
mov dx, offset max1 ; Address of the input structure
mov ah, 0Ah
int 21h
mov al, charRead1 ; Length of the string
mov ah, 0 ; Extending is needed because size1 is a word-sized variable
mov size1, ax
第二个字符串也是如此。
正在输出
Displaying numbers with DOS 详细解释了如何打印 16 位寄存器的值 AX
。
mov ah, 9
lea dx, bx
int 21h
当您使用 DOS.PrintString 函数 09h 时,DOS 期望在 DX
中有一个指向字符串的指针。 BX
代码中的寄存器包含一个数字,您仍然需要将其转换为一串数字。 link 中的 'method 2' 代码段会执行此操作,并且还会使用 DOS.PrintCharacter 立即显示字符
函数 02h。 Displaying characters with DOS or BIOS 有很多输出函数的信息。您只需在省略号处插入代码片段即可。
mov ax, bx
push bx ;(0)
...
pop bx ;(0)
由于这是我的原始代码,我会为您复制...
mov ax,bx
push bx ;(0)
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
pop bx ;(0)
(0) 必须保留 BX
,因为您需要再次使用该值来显示不同字符的数量。
您知道您不必“计算”不同的字符,因为您可以获得最长字符串的大小并减去相等字符的数量。但是你为什么不在你的程序中做那个减法?
mov ax,size2 ;SizeLongestString
sub ax,bx ; minus EqualCharacters
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
所以好像我们插入了两次代码来转换和显示一个数字。这是将它放入子例程然后 call
两次的一个很好的理由。见下文。
将所有内容放在一起,我们得到以下为 emu8086 (MASM) 制作的程序
.model small
.stack 100h
.data
Text1 DB "Please enter the shorter phrase: ",13,10,'$'
Text2 DB "Please enter the longer phrase: ",13,10,'$'
max1 DB 151
charRead1 DB 0
chars1 DB 151 dup(0)
max2 DB 151
charRead2 DB 0
chars2 DB 151 dup(0)
TextoEquals DB "Number of equal characters: ",13,10,'$'
TextoDiffer DB "Number of different characters: ",13,10,'$'
linefeed DB 13, 10, "$"
size1 DW 0
size2 DW 0
.code
AllMacros:
PrintText MACRO str
MOV AH, 09h
LEA DX, str
INT 21h
ENDM
PrintCls MACRO
mov ah, 09h
mov dx, offset linefeed
int 21h
ENDM
Inicio:
mov ax, @data
mov ds, ax
mov es, ax
PrintText Text1
mov dx, offset max1
mov ah, 0Ah
int 21h
mov al, charRead1
mov ah, 0
mov size1, ax
PrintCls
PrintText Text2
mov dx, offset max2
mov ah, 0Ah
int 21h
mov al, charRead2
mov ah, 0
mov size2, ax
PrintCls
cld ; Clear direction flag
xor bx, bx ; Reset counter
mov si, offset chars1 ; Pointer to shorter string
mov dx, size1 ; Length of the shorter string
NextChar:
lodsb ; Fetch next character from shorter string
mov di, offset chars2 ; Pointer to longer string
mov cx, size2 ; Length of the longer string
repne scasb
jne NotFound
inc bx ; Increment counter
mov byte ptr [di-1], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
PrintText TextoEquals
mov ax, bx
call DisplayAX
PrintCls
PrintText TextoDiffer
mov ax, size2
sub ax, bx
call DisplayAX
mov ax, 4C00h ; DOS.Terminate
int 21h
DisplayAX:
push bx ;(0)
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
pop bx ;(0)
ret
END Inicio
永远不要忘记正确退出程序。对于 DOS,首选方法是使用 DOS.Terminate 函数 4Ch.
[最近加题]
I don't want to use string instructions
这对我来说没问题。只需用等效代码替换 lodsb
和 repne scasb
字符串指令:
xor bx, bx ; Reset counter
mov si, offset chars1 ; Pointer to shorter string
mov dx, size1 ; Length of the shorter string
NextChar:
mov al, [si] ; Fetch next character from shorter string
inc si
mov di, offset chars2 ; Pointer to longer string
mov cx, size2 ; Length of the longer string
Scan:
cmp al, [di]
je Found
inc di
loop Scan
jmp NotFound
Found:
inc bx ; Increment counter
mov byte ptr [di], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
严格来说已经不用设置了ES
,但是留着对程序没有影响
程序将要求 2 个字符串(例如,最大长度为 150 个字符)。
输入
字符串 1: "Hello this is the first string"
字符串 2: "Woops this string is different and longer!"
输出
Number of equal characters = 26
Number of different characters = 15
我记得我们不必“计算”不同的字符,因为我们可以获得最长字符串的大小并减去相等字符的数量,但我不知道如何先做这个比较(相等字符的个数)。
我该怎么做?我可以为那个比较做一个宏吗?
更新
我用的是EMU8086,MASM。我不想使用字符串指令。
这是我当前的代码,按照评论中的说明进行操作,但我仍然无法正常工作。
.model small
.stack 100h
.data
Text1 DB "Please enter the first phrase: ",13,10,'$'
Text2 DB "Please enter the second phrase: ",13,10,'$'
max1 DB 151 ;We add one to 150 for the ENTER
charRead1 DB 0
max2 DB 151 ;We add one to 150 for the ENTER
charRead2 DB 0
TextoEquals DB "Number of equal characters: ",13,10,'$'
TextoDiffer DB "Number of different characters: ",13,10,'$'
linefeed DB 13, 10, "$"
size1 dw 0000h
size2 dw 0000h
.code
AllMacros:
MagicFunction MACRO
cld ; Clear direction flag
xor bx, bx ; Reset counter
mov si, OFFSET max1 ; Pointer to shorter string
mov dx, size1 ; Length of the shorter string
NextChar:
lodsb ; Fetch next character from shorter string
mov di, offset max2 ; Pointer to longer string
mov cx, size2 ; Length of the longer string
repne
scasb
jne NotFound
inc bx ; Increment counter
mov byte ptr [di-1], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
ENDM
PrintText MACRO str
MOV AH, 09h
LEA DX, str
INT 21h
ENDM
PrintCls MACRO
mov ah, 09h
mov dx, offset linefeed
int 21h
ENDM
Inicio:
mov ax, @data
mov ds, ax
PrintText Text1
mov ah, 0Ah
lea dx, max1
mov size1, offset max1
int 21h
PrintCls
PrintText Text2
mov ah, 0Ah
lea dx, max2
mov size2, offset max2
int 21h
PrintCls
MagicFunction
PrintText TextoEquals
mov ah, 9
lea dx, bx
int 21h
PrintCls
PrintText TextoDiffer
mov ah, 9
lea dx, dx
int 21h
END Inicio
你会如何在纸上做到这一点?
您将依次考虑较短字符串中的每个字符,并尝试在较长字符串中找到它。如果您找到了该字符,那么您将递增计数器,并从较长的字符串中删除该字符,这样就无法再次找到该字符。
发件人:
-- ----------- --------- ----
Hello this is the first string
Woops this string is different and longer!
- ----------------- -- --- -- -
收件人:
Hello这是the第一个st环(剩余4个字符)
==> 30 - 4 = 26 是 'equal'
Woops 这个字符串是 diff erent and longer!(剩余 16 个字符)
==> 16 个是 'different'
在汇编代码中它可能如下所示:
S1 db "Hello this is the first string"
S2 db "Woops this string is different and longer!"
...
cld ; Clear direction flag
xor bx, bx ; Reset counter
mov si, S1 ; Pointer to shorter string
mov dx, 30 ; Length of the shorter string
NextChar:
lodsb ; Fetch next character from shorter string
mov di, S2 ; Pointer to longer string
mov cx, 42 ; Length of the longer string
repne scasb
jne NotFound
inc bx ; Increment counter
mov byte [di-1], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
scasb
将 AL
与 [es:di]
设置标志处的字节进行比较,并递增 DI
寄存器(前提是方向标志已清除)。
repne scasb
只要 CX
中的计数未用完且未设置零标志,就会一直比较。
I have in mind that we don't have to "calculate" the different characters as we can get the size of the longest string and subtract the number of equal characters...
没错。
上面的代码应该会产生下一个结果:
相等字符数 = 26
不同字符的数量 = 42 - 26 = 16
感谢您在问题中包含代码并适当地标记它。这次我可以写一篇完整的评论。请注意,另一个答案并不像您似乎暗示的那样半个答案。这个答案与你当时的问题是一致的。
How can I do this? Could I do a macro for that comparison?
我在之前的回答中已经提供的代码,并且您已经采用了,并不是真的要用作宏。如果有的话,它可以成为一个子例程,但我认为在您的程序中这样做没有任何好处。只需内联代码。
Inicio: mov ax, @data mov ds, ax
正如我在之前的回答中所写,scasb
指令使用了 ES
段寄存器。您应该将其设置为与 DS
段寄存器相同:
Inicio:
mov ax, @data
mov ds, ax
mov es, ax
正在输入
你写的地方:
max1 DB 151 ;We add one to 150 for the ENTER charRead1 DB 0
您忘记为您希望从用户那里收到的实际字符预留空间。接下来是它需要的样子:
max1 DB 151
charRead1 DB 0
chars1 DB 151 dup(0)
当您调用此 DOS 函数 0Ah 时,您还将在 charRead1 字节中收到实际输入的长度。这是您需要存储在 size1 变量中的值(不是像您的代码现在所做的输入结构的地址!):
mov ah, 0Ah lea dx, max1 mov size1, offset max1 <<<< is wrong int 21h
当然,您只能在 DOS 调用完成后执行此操作:
mov dx, offset max1 ; Address of the input structure
mov ah, 0Ah
int 21h
mov al, charRead1 ; Length of the string
mov ah, 0 ; Extending is needed because size1 is a word-sized variable
mov size1, ax
第二个字符串也是如此。
正在输出
Displaying numbers with DOS 详细解释了如何打印 16 位寄存器的值 AX
。
mov ah, 9 lea dx, bx int 21h
当您使用 DOS.PrintString 函数 09h 时,DOS 期望在 DX
中有一个指向字符串的指针。 BX
代码中的寄存器包含一个数字,您仍然需要将其转换为一串数字。 link 中的 'method 2' 代码段会执行此操作,并且还会使用 DOS.PrintCharacter 立即显示字符
函数 02h。 Displaying characters with DOS or BIOS 有很多输出函数的信息。您只需在省略号处插入代码片段即可。
mov ax, bx
push bx ;(0)
...
pop bx ;(0)
由于这是我的原始代码,我会为您复制...
mov ax,bx
push bx ;(0)
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
pop bx ;(0)
(0) 必须保留 BX
,因为您需要再次使用该值来显示不同字符的数量。
您知道您不必“计算”不同的字符,因为您可以获得最长字符串的大小并减去相等字符的数量。但是你为什么不在你的程序中做那个减法?
mov ax,size2 ;SizeLongestString
sub ax,bx ; minus EqualCharacters
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
所以好像我们插入了两次代码来转换和显示一个数字。这是将它放入子例程然后 call
两次的一个很好的理由。见下文。
将所有内容放在一起,我们得到以下为 emu8086 (MASM) 制作的程序
.model small
.stack 100h
.data
Text1 DB "Please enter the shorter phrase: ",13,10,'$'
Text2 DB "Please enter the longer phrase: ",13,10,'$'
max1 DB 151
charRead1 DB 0
chars1 DB 151 dup(0)
max2 DB 151
charRead2 DB 0
chars2 DB 151 dup(0)
TextoEquals DB "Number of equal characters: ",13,10,'$'
TextoDiffer DB "Number of different characters: ",13,10,'$'
linefeed DB 13, 10, "$"
size1 DW 0
size2 DW 0
.code
AllMacros:
PrintText MACRO str
MOV AH, 09h
LEA DX, str
INT 21h
ENDM
PrintCls MACRO
mov ah, 09h
mov dx, offset linefeed
int 21h
ENDM
Inicio:
mov ax, @data
mov ds, ax
mov es, ax
PrintText Text1
mov dx, offset max1
mov ah, 0Ah
int 21h
mov al, charRead1
mov ah, 0
mov size1, ax
PrintCls
PrintText Text2
mov dx, offset max2
mov ah, 0Ah
int 21h
mov al, charRead2
mov ah, 0
mov size2, ax
PrintCls
cld ; Clear direction flag
xor bx, bx ; Reset counter
mov si, offset chars1 ; Pointer to shorter string
mov dx, size1 ; Length of the shorter string
NextChar:
lodsb ; Fetch next character from shorter string
mov di, offset chars2 ; Pointer to longer string
mov cx, size2 ; Length of the longer string
repne scasb
jne NotFound
inc bx ; Increment counter
mov byte ptr [di-1], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
PrintText TextoEquals
mov ax, bx
call DisplayAX
PrintCls
PrintText TextoDiffer
mov ax, size2
sub ax, bx
call DisplayAX
mov ax, 4C00h ; DOS.Terminate
int 21h
DisplayAX:
push bx ;(0)
mov bx,10 ;CONST
xor cx,cx ;Reset counter
.a: xor dx,dx ;Setup for division DX:AX / BX
div bx ; -> AX is Quotient, Remainder DX=[0,9]
push dx ;(1) Save remainder for now
inc cx ;One more digit
test ax,ax ;Is quotient zero?
jnz .a ;No, use as next dividend
.b: pop dx ;(1)
add dl,"0" ;Turn into character [0,9] -> ["0","9"]
mov ah,02h ;DOS.DisplayCharacter
int 21h ; -> AL
loop .b
pop bx ;(0)
ret
END Inicio
永远不要忘记正确退出程序。对于 DOS,首选方法是使用 DOS.Terminate 函数 4Ch.
[最近加题]
I don't want to use string instructions
这对我来说没问题。只需用等效代码替换 lodsb
和 repne scasb
字符串指令:
xor bx, bx ; Reset counter
mov si, offset chars1 ; Pointer to shorter string
mov dx, size1 ; Length of the shorter string
NextChar:
mov al, [si] ; Fetch next character from shorter string
inc si
mov di, offset chars2 ; Pointer to longer string
mov cx, size2 ; Length of the longer string
Scan:
cmp al, [di]
je Found
inc di
loop Scan
jmp NotFound
Found:
inc bx ; Increment counter
mov byte ptr [di], 0 ; Strike by replacing with zero
NotFound:
dec dx ; Repeat for all characters in shorter string
jnz NextChar
严格来说已经不用设置了ES
,但是留着对程序没有影响