首页
社区
课程
招聘
这是一个什么样的字符串?
发表于: 2006-10-28 13:34 5587

这是一个什么样的字符串?

2006-10-28 13:34
5587
不知有人读过这段程序没有:罗云彬的<<Windows环境下32位汇编语言程序设计>>中的WordCound程序。其中在_WalkTree子程序中,该子程序中有一个局部变量local @szWord[52]:byte,不知为什么定义为52个BYTE,因为所读文件sample.txt中最长的单词为12个字符,所以它定义不能小于12,如果把它定义为12,则程序会出错,定义为13则成功。该变量又作为递归调用自己的一个参数,
但请注意:在递归调用自己这前,ecx是指向这个串的指针,word ptr[ecx],ax似乎一次就要占用两个BYTE,这样的话,12个字符要占用24个BYTE,定义该变量为13个BYTE也不够用,但这样程序却没有出错,这是什么原因?而如果将word ptr[ecx],ax改为byte ptr[ecx],al(因为一个字符用一个byte就够了),在结果文件中却是一堆乱码。这使我感到非常奇怪,下面我把它贴出来,烦请给一个回答。

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Sample code for < Win32ASM Programming >
; by 罗云彬, http://asm.yeah.net
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; WordCount.asm
; 文件读写例子 ―― 打开文本文件进行单词统计,然后创建结果文件
; 读写文件操作使用文件操作函数。
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff WordCount.asm
; rc WordCount.rc
; Link /subsystem:windows WordCount.obj WordCount.res
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .386
                .model flat, stdcall
                option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include                windows.inc
include                user32.inc
includelib        user32.lib
include                kernel32.inc
includelib        kernel32.lib
include                comdlg32.inc
includelib        comdlg32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN        equ                1000
DLG_MAIN        equ                100
IDC_FILE        equ                101
IDC_BROWSE        equ                102

WORD_COUNT        struct

lpLetter        dd        26 dup (?)
dwCount                dd        ?
dwDepth                dd        ?

WORD_COUNT        ends
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .data?

hInstance        dd        ?
hWinMain        dd        ?
szFileName        db        MAX_PATH dup (?)
szBuffer        db        4096 dup (?)
stWordCount        WORD_COUNT        <>
dwCount                dd        ?

dwOption        dd        ?
;F_COUNTING        equ        00000001h
;F_FILEEND        equ        00000002h

;szBuf        db 8 dup ( ? )

                .const
szFileExt        db        '全部文件',0,'*.*',0,0
szLogExt        db        '.log',0
szErrOpenFile        db        '无法打开文件!',0
szErrCreateFile        db        '无法建立记录文件!',0
szFmtWord        db        '%5d (%3d‰) %s',0dh,0ah,0
szSuccees        db        '统计成功,请查看记录文件%s',0
szSucceesCap        db        '统计成功',0

;szFmtd db '%d',0
;szFmts db '%s',0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

                .code

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 遍历树并输出结果
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WalkTree        proc        _hFile,_lpWC,_lpsz
                local        @dwTemp
                local        @szWord[52]:byte

                pushad
                mov        esi,_lpWC
                assume        esi:ptr WORD_COUNT
                .if        [esi].dwDepth && [esi].dwCount
;********************************************************************
; 计算百分比并写入log文件
;********************************************************************
                        mov        eax,[esi].dwCount
                        mov        ecx,1000
                        mul        ecx
                        mov        ecx,dwCount
                        .if        ecx
                                div        ecx
                        .else
                                mov        eax,0
                        .endif
                        invoke        wsprintf,addr szBuffer,addr szFmtWord,[esi].dwCount,eax,_lpsz
                       
                        ;push esi
                        ;push eax
                        ;mov esi,_lpsz
                        ;lodsb
                        ;.while eax
                        ;invoke         wsprintf,addr szBuf, addr szFmtd,eax
                        ;invoke MessageBox,hWinMain,addr szBuf,NULL,MB_OK
                        ;invoke MessageBox,hWinMain,NULL,NULL,MB_OK
                        ;.endw
                        ;pop eax
                        ;pop esi
                        ;invoke MessageBox,hWinMain,_lpsz,NULL,MB_OK
                       
                        invoke        lstrlen,addr szBuffer
                        mov        ecx,eax
                        invoke        WriteFile,_hFile,addr szBuffer,ecx,addr @dwTemp,NULL
                .endif
;********************************************************************
; 如果有下层节点则递规调用
;********************************************************************
                mov        @dwTemp,0
                .while        @dwTemp < 26
                        mov        ebx,@dwTemp
                        mov        ebx,dword ptr [esi+ebx*4]
                        .if        ebx
                                invoke        lstrcpy,addr @szWord,_lpsz
                                invoke        lstrlen,addr @szWord
                                lea        ecx,@szWord
                                add        ecx,eax
                                mov        eax,@dwTemp
                                add        al,'a'
                                mov        word ptr [ecx],ax
                                invoke        _WalkTree,_hFile,ebx,addr @szWord
                        .endif
                        inc        @dwTemp
                .endw
;********************************************************************
; 释放节点
;********************************************************************
                .if        [esi].dwDepth
                        invoke        GlobalFree,esi
                .endif
                popad
                assume        esi:ptr WORD_COUNT
                ret

_WalkTree        endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 根据数据建立树节点或增加节点计数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CountLetter        proc        _dwLetter
                ;local        @dwIndex

                assume        edi:ptr WORD_COUNT
                or        al,20h                                ;转换成小写字母
                .if        (al >= 'a') && (al <= 'z')        ;字母
;********************************************************************
; 如果是字母则按edi指针继续搜寻下一节点,找到则更新指针,
; 未找到则建立一个新的节点
;********************************************************************
                        sub        al,'a'
                        movzx        eax,al
                        .if        dword ptr [edi+eax*4]
                                mov        edi,dword ptr [edi+eax*4]
                        .else
                                mov        ebx,[edi].dwDepth
                                .if        ebx <        50                                                       

; 单词长度不超过50 bytes
                                        push        eax
                                        invoke        GlobalAlloc,GPTR,sizeof WORD_COUNT;GPTR equ

GMEM_FIXED OR GMEM_ZEROINIT
                                        pop        ecx
                                        .if        eax
                                                mov        dword ptr [edi+ecx*4],eax
                                                mov        edi,eax
                                                inc        ebx
                                                mov        [edi].dwDepth,ebx
                                        .endif
                                .endif
                        .endif
                .else
;********************************************************************
; 不是字母表示一个单词已结束,增加单词计数
;********************************************************************
                        inc        [edi].dwCount
                        .if        [edi].dwDepth                                ; 非根结点
                                inc        dwCount
                        .endif
                        mov        edi,offset stWordCount
                .endif
                assume        edi:nothing
                ret

_CountLetter        endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CountWord        proc
                local        @hFile,@dwBytesRead
                local        @szLogFile[MAX_PATH]:byte
                local        @szBuffer
                invoke        RtlZeroMemory,addr stWordCount,sizeof stWordCount
;********************************************************************
; 打开文件
;********************************************************************
                invoke        CreateFile,addr szFileName,GENERIC_READ,FILE_SHARE_READ,0,\
                        OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
                .if        eax ==        INVALID_HANDLE_VALUE
                        invoke        MessageBox,hWinMain,addr szErrOpenFile,NULL,MB_OK or

MB_ICONEXCLAMATION
                        ret
                .endif
                mov        @hFile,eax
;********************************************************************
; 循环读出文件并处理每个字节
;********************************************************************
                xor        eax,eax
                mov        @dwBytesRead,eax
                mov        dwCount,eax
                mov        edi,offset stWordCount
                .while        TRUE
                        .if        @dwBytesRead
                                lodsb
                                dec        @dwBytesRead
                                invoke        _CountLetter,eax
                        .else
                                mov        esi,offset szBuffer
                                invoke        ReadFile,@hFile,addr szBuffer,sizeof szBuffer,addr

@dwBytesRead,0
                                .break        .if !@dwBytesRead
                        .endif
                .endw
                invoke        _CountLetter,0
                invoke        CloseHandle,@hFile
;********************************************************************
; 输出记录文件
;********************************************************************
                invoke        lstrcpy,addr @szLogFile,addr szFileName
                invoke        lstrcat,addr @szLogFile,addr szLogExt
                invoke        CreateFile,addr @szLogFile,GENERIC_WRITE,FILE_SHARE_READ,\
                        0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
                .if        eax !=        INVALID_HANDLE_VALUE
                        mov        @hFile,eax
                        mov        @szBuffer,0
                        mov        edi,offset stWordCount
                        invoke        _WalkTree,@hFile,edi,addr @szBuffer
                        invoke        CloseHandle,@hFile
                        invoke        wsprintf,addr szBuffer,addr szSuccees,addr @szLogFile
                        invoke        MessageBox,hWinMain,addr szBuffer,addr szSucceesCap,MB_OK or

MB_ICONINFORMATION
                .else
                        invoke        MessageBox,hWinMain,addr szErrCreateFile,NULL,MB_OK or

MB_ICONEXCLAMATION
                .endif
                ret

_CountWord        endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcDlgMain        proc        uses ebx edi esi hWnd,wMsg,wParam,lParam
                local        @stOpenFileName:OPENFILENAME

                mov        eax,wMsg
                .if        eax ==        WM_CLOSE
                        ;.if        ! (dwOption & F_COUNTING)
                        .if        ! dwOption
                                invoke        EndDialog,hWnd,NULL
                        .endif
;********************************************************************
                .elseif        eax ==        WM_INITDIALOG
                        push        hWnd
                        pop        hWinMain
                        invoke        LoadIcon,hInstance,ICO_MAIN
                        invoke        SendMessage,hWnd,WM_SETICON,ICON_BIG,eax
                        invoke        SendDlgItemMessage,hWnd,IDC_FILE,EM_SETLIMITTEXT,MAX_PATH,0
;********************************************************************
                .elseif        eax ==        WM_COMMAND
                        mov        eax,wParam
                        .if        ax ==        IDC_BROWSE
;********************************************************************
                                invoke        RtlZeroMemory,addr @stOpenFileName,sizeof OPENFILENAME
                                mov        @stOpenFileName.lStructSize,SIZEOF @stOpenFileName
                                mov        @stOpenFileName.Flags,OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST
                                push        hWinMain
                                pop        @stOpenFileName.hwndOwner
                                mov        @stOpenFileName.lpstrFilter,offset szFileExt
                                mov        @stOpenFileName.lpstrFile,offset szFileName
                                mov        @stOpenFileName.nMaxFile,MAX_PATH
                                invoke        GetOpenFileName,addr @stOpenFileName
                                .if        eax
                                        invoke        SetDlgItemText,hWnd,IDC_FILE,addr szFileName
                                .endif
;********************************************************************
                        .elseif        ax ==        IDC_FILE
                                invoke        GetDlgItemText,hWnd,IDC_FILE,addr szFileName,MAX_PATH
                                mov        ebx,eax
                                invoke        GetDlgItem,hWnd,IDOK
                                invoke        EnableWindow,eax,ebx
;********************************************************************
                        .elseif        ax ==        IDOK
                                invoke        GetDlgItem,hWnd,IDC_FILE
                                invoke        EnableWindow,eax,FALSE
                                invoke        GetDlgItem,hWnd,IDC_BROWSE
                                invoke        EnableWindow,eax,FALSE
                                invoke        GetDlgItem,hWnd,IDOK
                                invoke        EnableWindow,eax,FALSE
                                mov dwOption, TRUE
                                ;or        dwOption,F_COUNTING
                                call        _CountWord
                                ;and        dwOption,not (F_COUNTING or F_FILEEND)
                                mov dwOption, FALSE
                                invoke        GetDlgItem,hWnd,IDC_FILE
                                invoke        EnableWindow,eax,TRUE
                                invoke        GetDlgItem,hWnd,IDC_BROWSE
                                invoke        EnableWindow,eax,TRUE
                                invoke        GetDlgItem,hWnd,IDOK
                                invoke        EnableWindow,eax,TRUE
                        .endif
;********************************************************************
                .else
                        mov        eax,FALSE
                        ret
                .endif
                mov        eax,TRUE
                ret

_ProcDlgMain        endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
                invoke        GetModuleHandle,NULL
                mov        hInstance,eax
                invoke        DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,NULL
                invoke        ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                end        start

;资源文件
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include                <resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define        ICO_MAIN                1000
#define        DLG_MAIN                100
#define IDC_FILE                101
#define IDC_BROWSE                102
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN        ICON                "Main.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 141, 208, 201, 41
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "单词统计"
FONT 9, "宋体"
{
LTEXT "文件名", -1, 7, 8, 25, 8
EDITTEXT IDC_FILE, 35, 5, 160, 12, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
PUSHBUTTON "浏览(&B)", IDC_BROWSE, 115, 22, 40, 14
PUSHBUTTON "统计(&S)", IDOK, 155, 22, 40, 14, WS_DISABLED | WS_TABSTOP
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Sample 文件:

He's one of my best friend, a very very good man and has a very very good job
and their relationship used to be so charming and stable, but now changed.

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 2384
活跃值: (766)
能力值: (RANK:410 )
在线值:
发帖
回帖
粉丝
2
ECX指向的是UNICODE字符。所以要使用两个字节。
2006-10-28 13:55
0
雪    币: 207
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
首先感谢小虾能经常回答我的问题。
但:
1,既然ECX指向UNICODE字符,@szWord 即使只有13个字节也能成功,而文件中最长的单词有12个字符,至少需要24个字节(还不包括结尾的0)。
2,为什么要使ECX指向UNICODE字符,指向一个单字节字符不是也可以吗?
3,在Makefile文件中并没有UNICODE编译选项。

附:MAKEFILE文件
NAME = WordCount
OBJS = $(NAME).obj
RES  = $(NAME).res

LINK_FLAG = /subsystem:windows
ML_FLAG = /c /coff

$(NAME).exe: $(OBJS) $(RES)
        Link $(LINK_FLAG) $(OBJS) $(RES)

.asm.obj:
        ml $(ML_FLAG) $<
.rc.res:
        rc $<

clean:
        del *.obj
        del *.res
2006-10-28 14:13
0
雪    币: 207
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
没人回答么?郁闷
2006-10-28 20:04
0
雪    币: 179
活跃值: (131)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
5
不是有注释么,单词长度不超过50个字节,再加上0000就是52了
2006-10-29 00:22
0
游客
登录 | 注册 方可回帖
返回
//