我是汇编语言编程的完全初学者。我需要帮助编写汇编语言程序来从用户那里获取字符串,计算并显示用户输入的字符串中每个单词出现的次数。
例如,如果用户键入:
Hello Hello what is new Hello what is not new
输出应为:
Hello 3
what 2
is 2
not 1
new 2
我在下面的代码中给出了字符串中字符的频率。但是,我不知道如何编辑以便我可以跟踪单词而不仅仅是字符,然后能够以相应的频率显示这些单词。
INCLUDE Irvine32.inc
Get_frequencies PROTO,
pString:PTR BYTE, ; points to string
pTable:PTR DWORD ; points to frequency table
.data
freqTable DWORD 256 DUP(0)
;aString BYTE 1,2,"This is extremely difficult for the experienced",0
aString BYTE 80 DUP(0),0
str1 BYTE "*** Constructing a Frequency Table *** (DEMO)",
0dh,0ah,0dh,0ah,
"Enter between 1 and 80 characters: ",0
.code
main PROC
call Clrscr
mov edx,OFFSET str1
call WriteString
mov ecx,SIZEOF aString - 1
mov edx,OFFSET aString
call ReadString
INVOKE Get_frequencies, ADDR aString, ADDR freqTable
call DisplayTable
exit
main ENDP
;-------------------------------------------------------------
Get_frequencies PROC,
pString:PTR BYTE, ; points to string
pTable:PTR DWORD ; points to frequencey table
;
; Constructs a character frequency table. Each array position
; is indexed by its corresponding ASCII code.
;
; Returns: Each entry in the table contains a count of how
; many times that character occurred in the string.
;-------------------------------------------------------------
mov esi,pString
mov edi,pTable
cld ; clear Direction flag (forward)
L1: mov eax,0 ; clear upper bits of EAX
lodsb ; AL = [ESI], inc ESI
cmp al,0 ; end of string?
je Exit_proc ; yes: exit
shl eax,2 ; multiply by 4
inc DWORD PTR [edi + eax] ; inc table[AL]
jmp L1 ; repeat loop
Exit_proc:
ret
Get_frequencies ENDP
;-------------------------------------------------------------
DisplayTable PROC
;
; Display the non-empty entries of the frequency table.
; This procedure was not required, but it makes it easier
; to demonstrate that Get_frequencies works.
;-------------------------------------------------------------
.data
colonStr BYTE ": ",0
.code
call Crlf
mov ecx,LENGTHOF freqTable ; entries to show
mov esi,OFFSET freqTable
mov ebx,0 ; index counter
L1: mov eax,[esi] ; get frequency count
cmp eax,0 ; count = 0?
jna L2 ; if so, skip to next entry
mov eax,ebx ; display the index
call WriteChar
mov edx,OFFSET colonStr ; display ": "
call WriteString
mov eax,[esi] ; show frequency count
call WriteDec
call Crlf
L2: add esi,TYPE freqTable ; point to next table entry
inc ebx ; increment index
loop L1
call Crlf
ret
DisplayTable ENDP
END main
这是我到目前为止试图实施彼得答案中建议的蛮力搜索的原因:
.data
str2 BYTE "one two three",0
.code
main proc
mov edi,OFFSET str2
Mov esi,edi
Mov Ecx, 0 ;reset ecx to 0
Not Ecx ;set Ecx to -1 or highest possible integer
Mov Al, ' ' ;Initialize a1 to delimiter of (space) ' '
Cld ;Clear Direction Pointer
Repne Scasb ;scan edi one byte at a time until delimiter found
Not Ecx
Lea Eax, [ecx-1] ;Set Eax to index of found delimiter
Xchg Esi, Edi ;Take Edi which is now equal to string after found delimiter and put in esi
mov edx, esi
call WriteString
main endp
end main
这打印“两个三”,但我希望它打印“一个”。
答案 0 :(得分:3)
asm并没有什么特别的东西可以让你选择不同的算法,而不是你用C语言或你已经知道的任何其他语言。
这是作业吗?它似乎比你通常想要在asm中做的事情更复杂。但是,如果它只需要用于短字符串,那么不使用除char缓冲区之外的任何数据结构的非常糟糕的暴力算法将更容易实现(见下文)。
可打印的ASCII字符少于128个,因此计数表很小且很容易。
字符和单词之间的区别在于可能的单词数量是无限的(除了内存大小限制),因此您不能直接使用单词作为计数表的索引。即使使用4个字符的单词作为计数表的4字节索引,也会使计数表大小= 2 ^ 32个条目。
将计数表算法从字符调整为单词的最直接方法是某种dictionary data structure,如hash table,tree,甚至是{{3} }。每个条目都将一个单词映射到其计数器。
另一种选择是对单词列表进行排序,然后遍历计算重复项的排序列表。或者在排序时累积重复计数,即radix tree / trie。
这就是我可能做的事情:
;; tigher version of your char-count loop
xor eax, eax ; clear upper bits of EAX
L1:
lodsb ; AL = [ESI], inc ESI
inc DWORD PTR [edi + eax*4] ; inc table[AL]
test al,al ; Set flags based on AL
jz L1 ; loop if it's not the end of the string
;; fall through when done
;;dec DWORD PTR [edi] ; undo the count of the zero if you care
这会计算终止0,但会在循环中保存指令。使用循环外的更多代码,可以避免这种情况,同时仍然保持循环较小。
由于我们左移an interesting problem when your input word list is too big to fit in memory all at once ,我们不需要在循环内零eax。如果您关心表现,请使用as part of the addressing mode。
这对于长字符串执行起来非常糟糕,但对于非常短的字符串可能会非常好。主要优点是易于实现,但对于非常短的字符串(如128字节),它可能在树或哈希的性能上具有竞争力。
strstr(3)
)这花了很多时间复制字符。您可以使用rep movsb
执行此操作,我认为这仍然适用于重叠dst和src。
另一种选择是有一个第二个相等大小的缓冲区,所以你只删除整个字符串的一个副本,同时删除你正在计算的当前单词的所有出现位置:
从字符串A中的第二个单词开始:
count++
和dst-=length_of_current_countword
。当您检测到要查找的单词时,将目标指针(edi
)倒回到开头,以便将来复制将覆盖它。即你现在基本上可以免费复制它。