您的位置:首页 > 运维架构 > Linux

【Linux x86汇编踩坑】文件读写(二)读取文件转换大小写

2018-01-22 11:12 357 查看

【Linux x86汇编踩坑】文件读写(二)读取文件转换大小写

前言

转换大小写的思路很简单,来尝试用汇编写一个转换大小写的程序吧

转换大小写

由于在汇编中字符也是用ascii码存储的,那么思路就很清晰了,比如a对应的ascii码是97,而A对应的ascii码是65,那么将a转换为A只需要让a +(-32)即可,这个-32是他们之间的差,由于字母之间的ascii码是连续的,那么从a-z的小写字母我们都可以让它们加上-32来转换为大写字母。

知道了实现原理之后我们来实现一下这个函数。

#转换大小写
.type upperConversion, @function
#确定需要转换的字符的上下界限
.equ UP_CRITICAL, 'z'
.equ LOWER_CRITICAL, 'a'
#转换需要相加的数
.equ UPPER_CONVERSION, 'A' - 'a'
upperConversion:
pushl %ebp
movl %esp, %ebp
movl $BUFFER_SIZE, %ebx
#如果给定的缓冲区的长度为0
cmpl $0, %ebx
je conversion_exit

#索引
movl $0, %edi
#读取字节
read_byte:
movb BUFFER_DATA(,%edi,1), %cl
cmpb $UP_CRITICAL, %cl
jg next_byte
cmpb $LOWER_CRITICAL, %cl
jl next_byte

#转换大小写
addb $UPPER_CONVERSION, %cl
movb %cl, BUFFER_DATA(,%edi,1)

next_byte:
incl %edi
cmpl %edi, %ebx
jne read_byte

conversion_exit:
popl %ebp
ret


需要注意的是,BUFFER_DATA和BUFFER_SIZE分别对应我申请的缓冲区大小和缓冲区长度,具体代码在【Linux x86汇编踩坑】文件读写(一)读取文件并输出中,下面我也会附上完整的代码。

整体来看,上述代码用了一个循环read_byte来循环读取缓冲区里的字节,判断是否在a-z的范围内:

cmpb $UP_CRITICAL, %cl
jg next_byte
cmpb $LOWER_CRITICAL, %cl
jl next_byte


如果在范围内则进行转换大小写的操作,也就是给小写字母加上-32,然后将转换后的字符覆盖到原来缓冲区的位置

addb $UPPER_CONVERSION, %cl
movb %cl, BUFFER_DATA(,%edi,1)


如果在a-z的范围内,也就是不属于小写字母了,那么我们可以跳过,直接读取下一个字符

next_byte:
incl %edi
cmpl %edi, %ebx
jne read_byte


conversion_exit标签用于结束整个函数,把ebp弹出栈,通过ret把把函数返回地址弹出,随后eip寄存器指向函数的返回地址。

conversion_exit:
popl %ebp
ret


整个过程还是比较简单的,下面我附上完整的代码,整个程序的功能主要是读取一个文件的所有字符,并将小写字符转换为大写字符后进入标准输出流输出

toupper.s

.section .data
#系统调用号
.equ SYS_EXIT, 1
.equ SYS_READ, 3
.equ SYS_WRITE, 4
.equ SYS_OPEN, 5
.equ SYS_CLOSE, 6

#文件打开选项
.equ FILE_READONLY, 0
.equ FILE_WRITE, 03101

#系统调用中断
.equ SYS_INTERRUP, 0x80

#标准文件描述符
.equ STD_IN, 0
.equ STD_OUT, 1
.equ STD_ERR, 2

#文件结束符
.equ END_OF_FILE, 0

.section .bss
#申请缓冲区20byte
.equ BUFFER_SIZE, 50
.lcomm BUFFER_DATA, BUFFER_SIZE

.section .text
.globl _start
_start:
#文件名入栈
movl %esp, %ebp
pushl 8(%ebp)
# 打开文件
call openFile
addl $4, %esp
# 文件描述符保存在%eax
pushl %eax
# 读取文件
call readFile
addl $4, %esp
#转换大小写
call upperConversion
call writeFile
movl %eax, %ebx
movl $SYS_EXIT, %eax
int $SYS_INTERRUP

#系统调用打开文件
.type openFile, @function
openFile:
pushl %ebp
movl %esp, %ebp
#系统调用号
movl $SYS_OPEN, %eax
#文件名
movl 8(%ebp), %ebx
#读写意图
movl $FILE_READONLY, %ecx
#系统权限
movl $0666, %edx
#系统中断
int $SYS_INTERRUP
popl %ebp
ret

#读取文件
.type readFile, @function
readFile:
pushl %ebp
movl %esp, %ebp
read_loop:
#文件描述符
movl 8(%ebp), %ebx
#系统调用号
movl $SYS_READ, %eax
#缓冲区
movl $BUFFER_DATA, %ecx
#缓冲区大小
movl $BUFFER_SIZE, %edx
#系统调用
int $SYS_INTERRUP
cmpl $END_OF_FILE, %eax
jle loop_exit
jmp read_loop

#读取完毕
loop_exit:
#关闭文件
movl $SYS_CLOSE, %eax
movl 8(%ebp), %ebx
int $SYS_INTERRUP
popl %ebp
ret

#写入文件
.type writeFile, @function
writeFile:
pushl %ebp
movl %esp, %ebp
#文件描述符
movl $1, %ebx
#系统调用号
movl $SYS_WRITE, %eax
#缓冲区
movl $BUFFER_DATA, %ecx
#缓冲区大小
movl $BUFFER_SIZE, %edx
#系统调用
int $SYS_INTERRUP
popl %ebp
ret

#转换大小写 .type upperConversion, @function #确定需要转换的字符的上下界限 .equ UP_CRITICAL, 'z' .equ LOWER_CRITICAL, 'a' #转换需要相加的数 .equ UPPER_CONVERSION, 'A' - 'a' upperConversion: pushl %ebp movl %esp, %ebp movl $BUFFER_SIZE, %ebx #如果给定的缓冲区的长度为0 cmpl $0, %ebx je conversion_exit #索引 movl $0, %edi #读取字节 read_byte: movb BUFFER_DATA(,%edi,1), %cl cmpb $UP_CRITICAL, %cl jg next_byte cmpb $LOWER_CRITICAL, %cl jl next_byte #转换大小写 addb $UPPER_CONVERSION, %cl movb %cl, BUFFER_DATA(,%edi,1) next_byte: incl %edi cmpl %edi, %ebx jne read_byte conversion_exit: popl %ebp ret


在运行整个程序之前,先来编辑一下需要转换大小写的文件data,在data里写上这么一句,很简单的字符串对吧

hello world, MAN


尝试一下

as toupper.s -o toupper.o
ld toupper.o -o toupper
./toupper data
HELLO WORLD, MAN


successful!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: