汇编语言实验——文本比较

2.1实验内容

Windows界面风格实现两个文本文件内容的比对。若两文件内容一样,输出相应提示;若两文件不一样,输出对应的行号。

2.2实验环境

Microsoft Visual Studio 2017+masm 32

2.3实验思路

2.3.1 绘制界面

绘制界面上,我们调用WIN32库,首先初始化创建一个主窗口,之后在主窗口接受到WM_CREATE消息时创建3个按钮控件,分别为选择比较的第1个文件,选择比较的第2个文件,和执行比对三个功能。

_ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam  ;窗口过程
    local @stPs:PAINTSTRUCT
    local @stRect:RECT
    local @hDc

    mov eax,uMsg  ;uMsg是消息类型,如下面的WM_PAINT,WM_CREATE
    .if eax==WM_PAINT  ;如果想自己绘制客户区,在这里些代码,即第一次打开窗口会显示什么信息
        invoke BeginPaint,hWnd,addr @stPs
        mov @hDc,eax
        invoke EndPaint,hWnd,addr @stPs
    
    .elseif eax==WM_CLOSE  ;窗口关闭消息
        invoke DestroyWindow,hWinMain
        invoke PostQuitMessage,NULL
    .elseif eax==WM_CREATE  ;创建窗口
        invoke CreateWindowEx,NULL,offset button,offset szText1,\
        WS_CHILD or WS_VISIBLE,10,10,200,30,\ 
        hWnd,1,hInstance,NULL  ;1表示该按钮的句柄是1
        invoke CreateWindowEx,NULL,offset button,offset szText2,\
        WS_CHILD or WS_VISIBLE,10,50,200,30,\  
        hWnd,2,hInstance,NULL
        invoke CreateWindowEx,NULL,offset button,offset szText3,\
        WS_CHILD or WS_VISIBLE,10,90,200,30,\ 
        hWnd,3,hInstance,NULL
    .elseif eax==WM_COMMAND  ;点击时候产生的消息是WM_COMMAND
        mov eax,wParam  ;其中参数wParam里存的是句柄,如果点击了一个按钮,则wParam是那个按钮的句柄
        .if eax==1
            invoke _OpenFile,1
        .elseif eax==2
            invoke _OpenFile,2
        .elseif eax==3
            invoke _CompareFile
            ;如果没有不同的行号,则输出两个文件相同
            .if diffNum == 0
                invoke MessageBox,hWnd,offset SameContent,offset szBoxTitle,MB_OK+MB_ICONQUESTION
            ;反之输出不同的行号
            .else
                invoke MessageBox,hWnd,offset diffOut,offset szBoxTitle,MB_OK+MB_ICONQUESTION
                ;初始化diffOut
                invoke RtlZeroMemory,addr diffOut,sizeof diffOut
            .endif
        .endif

    .else  ;否则按默认处理方法处理消息
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
_ProcWinMain endp

_WinMain proc  ;窗口程序
    local @stWndClass:WNDCLASSEX  ;定义了一个结构变量,它的类型是WNDCLASSEX,一个窗口类定义了窗口的一些主要属性,图标,光标,背景色等,这些参数不是单个传递,而是封装在WNDCLASSEX中传递的。
    local @stMsg:MSG    ;还定义了stMsg,类型是MSG,用来作消息传递的  
    invoke GetModuleHandle,NULL  ;得到应用程序的句柄,把该句柄的值放在hInstance中,句柄是什么?简单点理解就是某个事物的标识,有文件句柄,窗口句柄,可以通过句柄找到对应的事物
    mov hInstance,eax
    invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass  ;将stWndClass初始化全0
    ;注册窗口类
    invoke LoadCursor,0,IDC_ARROW
    mov @stWndClass.hCursor,eax                 ;---------------------------------------
    push hInstance
    pop @stWndClass.hInstance
    mov @stWndClass.cbSize,sizeof WNDCLASSEX            ;这部分是初始化stWndClass结构中各字段的值,即窗口的各种属性
    mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW          
    mov @stWndClass.lpfnWndProc,offset _ProcWinMain 
    ;上面这条语句其实就是指定了该窗口程序的窗口过程是_ProcWinMain
    mov @stWndClass.hbrBackground,COLOR_WINDOW+1
    mov @stWndClass.lpszClassName,offset szClassName        ;---------------------------------------
    invoke RegisterClassEx,addr @stWndClass  ;注册窗口类,注册前先填写参数WNDCLASSEX结构
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,\  ;建立窗口
            offset szClassName,offset szCaptionMain,\  ;szClassName和szCaptionMain是在常量段中定义的字符串常量
            WS_OVERLAPPEDWINDOW,100,100,250,180,\   ;szClassName是建立窗口使用的类名字符串指针,这里是'MyClass',表示用'MyClass'类来建立这个窗口,这个窗口拥有'MyClass'的所有属性
            NULL,NULL,hInstance,NULL        ;如果改成'button'那么建立的将是一个按钮,szCaptionMain代表的则是窗口的名称,该名称会显示在标题栏中
    mov hWinMain,eax  ;建立窗口后句柄会放在eax中,现在把句柄放在hWinMain中。
    invoke ShowWindow,hWinMain,SW_SHOWNORMAL  ;显示窗口,注意到这个函数传递的参数是窗口的句柄,正如前面所说的,通过句柄可以找到它所标识的事物
    invoke UpdateWindow,hWinMain  ;刷新窗口客户区
    .while TRUE  ;进入无限的消息获取和处理的循环
        invoke GetMessage,addr @stMsg,NULL,0,0  ;从消息队列中取出第一个消息,放在stMsg结构中
        .break .if eax==0  ;如果是退出消息,eax将会置成0,退出循环
        invoke TranslateMessage,addr @stMsg  ;这是把基于键盘扫描码的按键信息转换成对应的ASCII码,如果消息不是通过键盘输入的,这步将跳过
        invoke DispatchMessage,addr @stMsg  ;这条语句的作用是找到该窗口程序的窗口过程,通过该窗口过程来处理消息
    .endw
    ret
_WinMain endp

2.3.2 读取文件

读取文件我们首先采用Comdlg32.lib提供的GetOpenFileName接口,从Windows对话框选择文件,读取文件的路径。之后将文件的路径存储在szFileName中,之后通过调用WIN32提供的CreateFile接口创建文件句柄,打开文件。

 ;定义OPENFILENAME变量
    local @stOF:OPENFILENAME
    ;初始化
    invoke RtlZeroMemory,addr @stOF,sizeof @stOF
    mov @stOF.lStructSize,sizeof @stOF
    push hWinMain
    pop @stOF.hwndOwner
    mov @stOF.lpstrFilter,offset szFilter
    ;flag标记打开的是文件1还是2
    .if flag==1
        mov @stOF.lpstrFile,offset szFileName1
    .elseif flag==2
        mov @stOF.lpstrFile,offset szFileName2
    .endif
    mov @stOF.nMaxFile,MAX_PATH
    mov @stOF.Flags,OFN_FILEMUSTEXIST OR OFN_PATHMUSTEXIST
    ;调用windows对话框打开文件,得到文件路径
    INVOKE GetOpenFileName,addr @stOF

2.3.3 文件对比

由于实验要求按行比较文本,若内容不同则输入行号,所以我们将按行读入打开文件的内容到字符数组buffer中,通过调用strcmp函数比较两个buffer中内容是否相同即可。

L1:
    inc _line
    ;初始化buffer,并读入一行数据
    invoke  RtlZeroMemory,addr szBuffer1,sizeof szBuffer1
    invoke _ReadLine,hFile1,offset szBuffer1
    ;返回值为buffer长度
    mov p1,eax
    invoke  RtlZeroMemory,addr szBuffer2,sizeof szBuffer2
    invoke _ReadLine,hFile2,offset szBuffer2
    mov p2,eax

L2:
    ;如果长度为0,则表示已读到文件结束
    cmp p1,0
    ;不等于0,则跳转L3继续比较p2
    jne L3
    ;比较p2长度,如果也为0,表示文件1和2都已读完,结束循环
    cmp p2,0
    je L5
    ;若p2不等于0,则表示buffer1为空,buffer2不为空,两者一定不等,记录行号
    invoke sprintf,addr diffTem,offset DiffContent,_line
    invoke lstrcat,addr diffOut,addr diffTem
    ;diffNum+1
    inc diffNum
    ;继续循环
    jmp L1
L3:
    ;比较p2,若不为空则表示p1,p2都不为空,调用strcmp比较
    cmp p2,0
    jne L4
    ;若p2为空,则表示buffer1不为空,buffer2为空,两者一定不等,记录行号
    invoke sprintf,addr diffTem,offset DiffContent,_line
    invoke lstrcat,addr diffOut,addr diffTem
    inc diffNum
    jmp L1
L4:
    ;调用strcmp比较
    invoke lstrcmp,offset szBuffer1,offset szBuffer2
    cmp eax,0
    ;若两者相同,继续循环
    je L1
    ;反之记录行号
    invoke sprintf,addr diffTem,offset DiffContent,_line
    invoke lstrcat,addr diffOut,addr diffTem
    inc diffNum
    jmp L1
L5:
    ;循环结束,关闭句柄
    invoke CloseHandle,hFile1
    invoke CloseHandle,hFile2

2.3.4 按行读入

读入文件内容采用WIN32提供的ReadFile接口一个字符一个字符的读入,并存储到buffer中,当读入到回车或者空时,则结束。

_ReadLine proc uses ebx,hFile:HANDLE,buffer:ptr byte
    ;指向实际读取字节数的指针
    local lpNum:dword
    ;用于保存读入数据的一个缓冲区
    local _str:byte
    ;ebx=buffer[0]
    mov ebx,buffer
    .while TRUE
        ;读入一个1个字符到_str中
        invoke ReadFile,hFile,addr _str,1,addr lpNum,NULL
        ;如果指针为空,退出循环
        .break .if !lpNum
        ;或者遇到换行号,退出循环
        .break .if _str==10

        ;将读入的字符赋给buffer
        mov al,_str
        mov [ebx],al
        ;ebx=buffer[i+1]
        inc ebx
    .endw
    ;最后一位赋0表示结束
    mov al,0
    mov [ebx],al
    ;调用strlen,结果存到eax中,返回eax
    invoke lstrlen,buffer
    ret
_ReadLine endp

2.3.5 输出

在每次比对结果不同时,将不同的行号存储为字符串存储到一起,最后调用MessageBox输出结果。

;如果没有不同的行号,则输出两个文件相同
            .if diffNum == 0
                invoke MessageBox,hWnd,offset SameContent,offset szBoxTitle,MB_OK+MB_ICONQUESTION
            ;反之输出不同的行号
            .else
                invoke MessageBox,hWnd,offset diffOut,offset szBoxTitle,MB_OK+MB_ICONQUESTION
                ;初始化diffOut
                invoke RtlZeroMemory,addr diffOut,sizeof diffOut
            .endif

2.4 实验结果

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值