x86字符串处理

字符串拷贝、查找、替换、大小写转化以及字典排序

Posted by Echo on May 14, 2018

字符串处理是我们在学习一门语言时不可或缺的练习,下面我们介绍几个简单的处理程序。

拷贝

功能简介:

功能:
	拷贝字符串,接受任意目的地址
	可在MAIN中设置目的地址相对源
	地址位置
样例:
	请在debug模式验证

代码演示:

;stack segment define
STACK1		SEGMENT	PARA	STACK
STACK_AERE	DW 		100H DUP(?)
STACK_BTM	EQU 	$ - STACK_AERE
STACK1 		ENDS

;data segment define
DATA1		SEGMENT
BUF			DB		10H DUP(0)
SRC			DB 		'Hello, x86!'
			DB 		'$'
LEN			EQU 	$ - SRC
DST			DB 		LEN DUP(0)
DATA1		ENDS

CODE1 		SEGMENT
			ASSUME 	CS:CODE1, SS:STACK1, DS:DATA1

;STRLEN		PROC
;STRLEN		ENDP			

;源地址SI,目的地址DI,拷贝长度CX
STRCPY		PROC
			PUSH	ES				;维护ES
			PUSH	DS
			POP 	ES				;ES与DS处于同一个段
			MOV		BX, SI			;暂存SI
			
			CMP 	SI, DI
			JZ 		BRK
			
			JAE		FORDER			;SI >= DI -> forward
			ADD 	SI, CX
			DEC 	SI
			CMP 	SI, DI			;SI+LEN<=DI -> forward
			JBE		FORDER
BACKER:		STD
			ADD 	DI, CX
			DEC 	DI
			JMP 	CPY
			
FORDER:		CLD	
			MOV 	SI, BX
			JMP 	CPY
			
CPY:		LODSB	
			STOSB
			LOOP	CPY
			
BRK:		POP 	ES
			RET
STRCPY 		ENDP		
			
MAIN		PROC 	FAR
			MOV 	AX, STACK1
			MOV		SS, AX
			MOV 	SP, STACK_BTM
			MOV 	AX, DATA1
			MOV 	DS, AX
			
			;设置源地址和目的地址
			MOV 	SI, OFFSET SRC
			;MOV 	DI, OFFSET DST	;3---SRC+len<=DST
			
			;2-start---SRC<DST<SRC+LEN
			MOV	 	DI, OFFSET SRC	
			ADD		DI, LEN
			SUB		DI, 2
			;2-end---SRC<DST<SRC+LEN
			
			;1-start---DST<=SRC
			;MOV		DI, OFFSET SRC	
			;SUB		DI, 2
			;1-end---DST<=SRC
			MOV		CX, LEN
			;拷贝字符串
			CALL 	STRCPY
	
EXIT:		MOV		AH, 4CH
			INT		21H
MAIN		ENDP
CODE1 		ENDS
			END 	MAIN

查找、替换

功能简介:

功能:
	输入母字符串,子字符串以及替换字符串
	输出替换后的结果
样例:
	输入:Hello world, my worlds.
		  world
		   -> x86
		  Hello x86, my x86s.

代码演示:

;stack segment define
STACK1		SEGMENT	PARA	STACK
STACK_AERE	DW 		100H DUP(?)
STACK_BTM	EQU 	$ - STACK_AERE
STACK1 		ENDS

;data segment define
DATA1		SEGMENT
FLAG		DW		0		;默认前移
DIFF		DW		0		;子串和替换串长度一致
RPSTR_BUF	DB 		20H
RPLEN		DB		?
RPSTR		DB 		1EH	DUP(?)
SBSTR_BUF	DB 		20H
SBLEN		DB		?
SBSTR		DB 		1EH	DUP(?)
SRC_BUF		DB 		0FFH
LEN			DB		?
SRC			DB 		0FEH DUP(?)
BUFF_		DB		010H DUP(?)
DATA1		ENDS

CODE1		SEGMENT
			ASSUME 	CS:CODE1, SS:STACK1, DS:DATA1
;将地址缓冲区首地址偏移存入DX
READ_STR	PROC
			MOV 	AH, 0AH
			INT 	21H
			RET
READ_STR	ENDP

REP_STR		PROC
			PUSH 	ES
			PUSH	DS
			POP 	ES
			
			MOV 	SI, OFFSET SRC 
			MOV 	CL, LEN
			AND		CX, 00FFH
LP_SRC:		PUSH 	CX
			PUSH	SI
			MOV		CL, SBLEN			;获取子串长度
			AND 	CX, 00FFH
			MOV 	DI, OFFSET SBSTR
			CLD
LP_CMP:		CMPSB
			JNZ		CONTINUE			
			LOOP	LP_CMP			;DS:[SI]==ES:[DI]->LP
			POP 	BX				;匹配串初始位置
			POP 	CX				;决定替换之后原串长度更新
			SUB		CL, SBLEN		;预置CX
			SBB		CH, 0
			INC		CX
			PUSH	CX
			PUSH 	BX
			CMP		FLAG, 0
			JZ		MVFW				;剩余的串前移
MVBK:		ADD 	SI, CX
			DEC		SI
			MOV 	DI, SI
			ADD 	DI, DIFF
			STD
			REP		MOVSB
			JMP		REPLACE
MVFW:		MOV 	DI, SI
			SUB 	DI, DIFF
			CLD
			REP 	MOVSB
REPLACE:	POP		DI
			MOV		CL, RPLEN			;替换穿作为SI,原串作为DI
			AND 	CX, 00FFH
			MOV		SI, OFFSET RPSTR
			CLD
			REP		MOVSB
			POP 	CX
			MOV 	SI, DI
			LOOP 	LP_SRC

CONTINUE:	POP		SI
			INC		SI
			POP 	CX
			LOOP 	LP_SRC
;显示处理结果	
DEL_END:	MOV		BYTE PTR [SI], 24H
			MOV		AH, 9
			MOV		DX, OFFSET SRC
			INT		21H
			
			POP 	ES
			RET
REP_STR		ENDP


DISP_SP		PROC
			MOV		AH, 2		;打印空格
			MOV 	DL, 0AH
			INT 	21H
			MOV		AH, 2		;打印箭头
			MOV 	DL, 2DH
			INT 	21H
			MOV		AH, 2		;
			MOV 	DL, 3EH
			INT 	21H
			MOV		AH, 2		;打印空格
			MOV 	DL, 20H
			INT 	21H
			RET
DISP_SP		ENDP

DISP_NL		PROC
			MOV		AH, 2		;打印空格
			MOV 	DL, 0AH
			INT 	21H
			RET
DISP_NL		ENDP

MAIN		PROC	FAR
			MOV 	AX, STACK1
			MOV		SS, AX
			MOV 	SP, STACK_BTM
			MOV 	AX, DATA1
			MOV 	DS, AX
	
			LEA		DX, SRC_BUF		;SRC缓冲区首地址偏移
			CALL	READ_STR
			CALL 	DISP_NL
			LEA 	DX, SBSTR_BUF	;SBSTR子串缓冲区首地址偏移
			CALL 	READ_STR
			CALL 	DISP_SP
			LEA		DX, RPSTR_BUF
			CALL 	READ_STR
			CALL	DISP_NL
			
			MOV 	CL, SBLEN		;子串长度
			AND 	CX, 00FFH
			MOV 	BL, RPLEN		;替换串长度
			AND 	BX, 00FFH		
			CMP 	BX, CX
			JBE		FW
			MOV 	FLAG, 1			;1代表后移
			SUB		BX, CX
			MOV 	DIFF, BX
			JMP		RP

FW:			SUB		CX, BX
			MOV 	DIFF, CX
			
RP:			CALL 	REP_STR
			
EXIT:		MOV		AH, 4CH
			INT		21H
MAIN		ENDP
CODE1		ENDS
			END 	MAIN

大小写转换

功能简介:

功能:
 	不区分大小写查找字符串
 	并输出所有index,若无匹配返回~
 	输入:母字符串
 		  子字符串
 	输出:查找结果列表,

样例:
 	输入:Tommy! Leave My tiMMy.
 		  my
 	输出:3D 13D 19D

代码演示:

;stack segment define
STACK1		SEGMENT	PARA	STACK
STACK_AERE	DW 		100H DUP(?)
STACK_BTM	EQU 	$ - STACK_AERE
STACK1 		ENDS

;data segment define
DATA1		SEGMENT
SBSTR_BUF	DB 		20H
SBLEN		DB		?
SBSTR		DB 		1DH	DUP(?)
SRC_BUF		DB 		07FH
LEN			DB		?
SRC			DB 		07CH DUP(?)
FLAG		DW		0
DATA1		ENDS

CODE1		SEGMENT
			ASSUME 	CS:CODE1, SS:STACK1, DS:DATA1

;将地址缓冲区首地址偏移存入DX
READ_STR	PROC
			PUSH	ES
			PUSH	DS
			POP		ES
			
			MOV 	AH, 0AH
			INT 	21H
			
			MOV		SI, DX
			INC		SI			;加1获得len
			MOV		CL, [SI]
			AND		CX, 00FFH
			INC		SI			;位移到字符串起始地址
			MOV		DI, SI
			CLD
LP1:		LODSB
			CMP		AL, 'a'
			JB		CONTINUE_RD
			CMP		AL, 'z'
			JA		CONTINUE_RD
			SUB		AL, 20H
CONTINUE_RD:STOSB		
			LOOP	LP1
			
			POP		ES
			RET
READ_STR	ENDP

DISP_NL		PROC
			MOV		AH, 2		;打印换行
			MOV 	DL, 0AH
			INT 	21H
			RET
DISP_NL		ENDP

;将AX中的数以10进制显示
DISP_10		PROC 	NEAR
			MOV 	CX, 0
LP_10_real:	MOV 	DX, 0
			MOV		BX, 10
			DIV 	BX			;商在AX,余数在DX
			PUSH 	DX			;余数入栈
			INC 	CX
			CMP 	AX, 0
			JNZ 	LP_10_real		
LP_10_num:	POP 	AX
			AND		AL, 0FH
			ADD 	AL, 30H
			MOV		AH, 2
			MOV 	DL, AL
			INT 	21H
			LOOP 	LP_10_num
			MOV		AH, 2		;打印10进制末尾D
			MOV 	DL, 44H
			INT 	21H
			MOV		AH, 2		;打印空格
			MOV 	DL, 20H
			INT 	21H
			RET
DISP_10		ENDP

FIND		PROC
			PUSH	ES
			PUSH	DS
			POP		ES

			MOV 	SI, OFFSET SRC 		
			MOV 	CL, LEN				;获得母字符串长度
			AND		CX, 00FFH
LP_SRC:		PUSH	CX
			PUSH	SI
			MOV		CL, SBLEN
			AND		CX, 00FFH
			MOV 	DI, OFFSET SBSTR
			CLD
LP_CMP:		CMPSB
			JNZ		CONTINUE_FD			
			LOOP	LP_CMP			;DS:[SI]==ES:[DI]->LP
			MOV		FLAG, 1			;标记成功匹配
			POP 	AX				;匹配串初始位置,需打印
			MOV		BX, OFFSET SRC
			SUB		AX, BX
			;PUSH	AX
			CALL	DISP_10
			;POP		SI
			POP 	CX				;决定替换之后原串长度更新
			SUB		CL, SBLEN		;预置CX
			SBB		CH, 0
			INC		CX
			LOOP	LP_SRC
			; PUSH	CX
			; PUSH 	BX
			
CONTINUE_FD:POP		SI
			INC		SI
			POP		CX
			LOOP	LP_SRC		
	
			CMP		FLAG, 1
			JZ		BRK
			MOV		AH, 2		;打印~表示不匹配
			MOV 	DL, 7EH
			INT 	21H
BRK:		MOV		AH, 2		;打印换行
			MOV 	DL, 0AH
			INT 	21H			
			POP		ES
			RET
FIND		ENDP
			
MAIN		PROC	FAR
			MOV 	AX, STACK1
			MOV		SS, AX
			MOV 	SP, STACK_BTM
			MOV 	AX, DATA1
			MOV 	DS, AX

			LEA		DX, SRC_BUF		;SRC缓冲区首地址偏移
			CALL	READ_STR		;读字符串并将小写变成大写
			CALL 	DISP_NL
			LEA 	DX, SBSTR_BUF	;SBSTR子串缓冲区首地址偏移
			CALL 	READ_STR
			CALL	DISP_NL
			
			CALL	FIND
			
EXIT:		MOV		AH, 4CH
			INT		21H			
MAIN		ENDP
CODE1		ENDS
			END 	MAIN

字典排序

功能简介:

功能:
	输入多个字符串(由字母组成),
	以空格间隔、回车结束,
	输出排序后的结果
样例:
	输入:
		My name is Echo
	输出:
		ECHO IS MY NAME

代码演示:

;stack segment define
STACK1		SEGMENT	PARA	STACK
STACK_AERE	DW 		100H DUP(?)
STACK_BTM	EQU 	$ - STACK_AERE
STACK1 		ENDS

;data segment define
DATA1		SEGMENT
IN_MSG		DB	'Please input strings separated by SPACE and ended by ENTER:(only letters)','$'
OUT_MSG		DB	'The dictionary sort result is:', '$'
TMP_BUF		DB	10H DUP(0)
TMP_LEN		DW	0
STR_ARR		DB	0FFH
			DB	?
STRS		DB	0FCH DUP(0)
ARR_LEN		DW	0
CMP_FLAG	DW	0		;0-不需要排序,1-需要排序
;INDEX_ARR	DW	0FFH DUP(0)
DATA1		ENDS

CODE1		SEGMENT
			ASSUME 	CS:CODE1, SS:STACK1, DS:DATA1
;将地址缓冲区首地址偏移存入DX
READ_STR	PROC
			PUSH	ES
			PUSH	DS
			POP		ES
			
			MOV 	AH, 0AH
			INT 	21H
			
			;LEA		BX, INDEX_ARR
			MOV		SI, DX
			INC		SI			;加1获得len
			MOV		CL, [SI]
			AND		CX, 00FFH
			INC		SI			;位移到字符串起始地址
			; MOV		[BX], SI
			; ADD		BX, 2
			MOV		DI, SI
			CLD
LP1_RD:		LODSB
			CMP		AL, 'a'
			JB		CONTINUE_RD
			CMP		AL, 'z'
			JA		CONTINUE_RD
			SUB		AL, 20H
CONTINUE_RD:CMP		AL, 20H
			JNZ		CONTINUE_R
			INC		ARR_LEN
			;MOV		[BX], SI
			;ADD		BX, 2
CONTINUE_R:	STOSB		
			LOOP	LP1_RD
			MOV		BYTE PTR [DI], 20H
;			MOV		BYTE PTR [DI+1], 0DH
			MOV		BYTE PTR [DI+1], '$'
			INC		ARR_LEN
			
			POP		ES
			RET
READ_STR	ENDP

DICSORT		PROC
LP1_DIC:	MOV		AX, 1			;标志位
			LEA		SI, STRS		;首个字符串起始index
			MOV		CX, ARR_LEN		;字符串的个数
			DEC		CX				;冒泡排序,比较n-1次
			MOV		DI, SI			;取两个index,DI为前字符串,SI为后字符串
LP2_DIC:	;MOV		SI, [BX+2]
			CALL	GET_NEXT		;从SI指向的字符串开始,将下一个字符串的指针存在SI中
			CALL	DIC_CMP			;比较并按两个输入字符串,指针为DI,SI[不能改变DI,SI],结果存入CMP_FLAG
			CMP 	CMP_FLAG, 0
			JZ		CONTINUE_DIC
			CALL 	CHG_STR			;交换SI,DI的两个字符串
			XOR 	AX, AX			;设立FLAG表示已交换
CONTINUE_DIC:
			MOV 	DI, SI
			LOOP	LP2_DIC
			CMP 	AX, 1
			JZ		EXIT_DIC		
			JMP		LP1_DIC

EXIT_DIC:	RET
DICSORT		ENDP

CHG_STR		PROC
			PUSH	ES
			PUSH	AX
			PUSH	CX
			PUSH	DI
			PUSH	SI	

			PUSH	DS
			POP		ES
			;前字符串拷贝到TMP中
			MOV		CX, SI
			SUB		CX, DI		;获得前字符串的长度
			MOV		TMP_LEN, CX
			MOV		SI, DI		;设定源地址
			LEA		DI, TMP_BUF
			CALL	STRCPY
			;后字符串拷贝到前字符串中
			POP		SI		;将后字符串取出作为源地址
			MOV		AX, SI	;将SI暂存到AX中
			POP		DI		;前字符串取出作为目的地址
			PUSH	DI		;入栈保存
			;PUSH	SI
			CALL	GET_NEXT	;获得后字符串长度
			MOV		CX, SI
			SUB		CX, AX
			MOV		SI, AX		;将AX返还给SI
			CALL	STRCPY		;由于是++拷贝,故拷贝完之后的DI作为下一次拷贝的目的地址
			PUSH	DI			;DI是下一个字符串新的起始地址,即CONTINUE_DIC中应该更新的SI
			;将前字符串拷贝到后字符串中
			LEA		SI, TMP_BUF
			MOV		CX, TMP_LEN
			CALL	STRCPY

			POP		SI
			POP		DI
			POP		CX
			POP		AX
			POP		ES
			RET
CHG_STR		ENDP

;源地址SI,目的地址DI,拷贝长度CX
STRCPY		PROC
			PUSH	ES				;维护ES
			PUSH	DS
			POP 	ES				;ES与DS处于同一个段
			MOV		BX, SI			;暂存SI
			
			CMP 	SI, DI
			JZ 		BRK
			
			JAE		FORDER			;SI >= DI -> forward
			ADD 	SI, CX
			DEC 	SI
			CMP 	SI, DI			;SI+LEN<=DI -> forward
			JBE		FORDER
BACKER:		STD
			ADD 	DI, CX
			DEC 	DI
			JMP 	CPY
			
FORDER:		CLD	
			MOV 	SI, BX
			JMP 	CPY
			
CPY:		LODSB	
			STOSB
			LOOP	CPY
			
BRK:		POP 	ES
			RET
STRCPY 		ENDP			

GET_NEXT	PROC
			PUSH	ES
			PUSH	AX
			PUSH	DS
			POP		ES

			CLD
LP_NEXT:	LODSB
			CMP		AL, 20H		;若是单个字符串结尾,则+1记录为下一个字符串开始index
			JNZ		LP_NEXT

			POP 	AX
			POP		ES
			RET
GET_NEXT	ENDP

;比较并按两个输入字符串,指针为DI,SI
DIC_CMP		PROC
			PUSH	ES
			PUSH	AX
			;PUSH	BX
			PUSH	CX
			PUSH	SI
			PUSH 	DI

			PUSH	DS
			POP		ES
			
			MOV 	CX, SI
			SUB		CX, DI		;将前一个字符串的长度作为比较长度
			CLD
			MOV		CMP_FLAG, 0	;清空排序标志位(两个内存数不能操作)
LP_CMP:		CMPSB
			JZ 		CONTINUE_CMP
			;JA		CMP1	
			JA		EXIT_CMP		;DI在前,SI在后,故采用JA	
			MOV		CMP_FLAG, 1
			JMP 	EXIT_CMP
CONTINUE_CMP:
			LOOP	LP_CMP	

EXIT_CMP:	POP		DI
			POP		SI
			POP		CX
			;POP		BX
			POP		AX
			POP		ES
			RET
DIC_CMP		ENDP

HINTS		PROC
			MOV 	AX, DATA1
			MOV 	DS, AX

			;LEA		DX, STRS
			MOV		AH, 9
			INT 	21H		
			MOV		AH, 2		;打印回车
			MOV 	DL, 0AH
			INT 	21H
			
			RET
HINTS 		ENDP

MAIN		PROC	FAR
			MOV 	AX, STACK1
			MOV		SS, AX
			MOV 	SP, STACK_BTM
			MOV 	AX, DATA1
			MOV 	DS, AX
			;input hints
			LEA		DX, IN_MSG
			CALL	HINTS

			LEA		DX, STR_ARR
			CALL	READ_STR		;读字符串并统一为大写
			CALL	DICSORT
			MOV		AH, 2		;打印回车
			MOV 	DL, 0AH
			INT 	21H
			;ouput hints
			LEA		DX, OUT_MSG
			CALL	HINTS
			;output disp
			LEA		DX, STRS
			CALL	HINTS

EXIT:		MOV		AH, 4CH
			INT		21H
MAIN		ENDP
CODE1		ENDS
			END 	MAIN

以上便是关于字符串操作的x86程序演示,引用请注明出处(https://echo-ji.github.io/2018/05/14/x86-字符串处理/)。