Linux汇编读取文件,4、Linux汇编——文件中结构化数据的操作(下)

5、读取记录

功能:程序读取每条记录,并显示每条记录中的“名”

记录读取功能主要执行以下步骤:

1、打开文件

2、读取一条记录

3、若到文件末尾,则退出;否则计算“名”的字符数

4、将“名”写入STDOUT中

5、输出换行符到STDOUT中

6、返回,并读取另一条记录

(1)字数统计函数

由于每条记录中,“名”的长度不定,因此需要一个函数来统计写入的字符数。该程序存于count-chars.s

#目的:对记录中字符数进行统计,遇到空字符结束

#输入:字符串地址

#输出:计数值返回到%eax中

#变量:   %ecx——字符计数

#        %al——当前字符

#        %edx——当前字符地址

.type count_chars, @function

.globl count_chars

.equ ST_STRING_START_ADDRESS, 8  #字符串开始地址

count_chars:    pushl %ebp

movl %esp, %ebp

movl $0, %ecx    #计数器从0开始

movl ST_STRING_START_ADDRESS(%ebp), %edx   #数据的起始地址

count_loop_begin:    movb (%edx), %al  #获取当前字符,采用间接寻址方式

cmpb $0, %al      #判断是否为空字符

je count_loop_end

incl %ecx

incl %edx

jmp count_loop_begin

count_loop_end:    movl %ecx, %eax   #结束循环,将统计的字数存入%eax中

popl %ebp

ret

(2)写一个换行符到STDOUT的函数

该程序存于文件write-newline.s文件中

.include "linux.s"

.globl write_newline

.type write_newline, @function

.section .data

newline:

.ascii "\n"

.section .text

.equ ST_FILEDES, 8

write_newline:    pushl %ebp

movl %esp, %ebp

movl $SYS_WRITE, %eax

movl ST_FILEDES(%ebp), %ebx

movl $newline, %ecx

movl $1, %edx

int $LINUX_SYSCALL

movl %ebp, %esp

popl %ebp

ret

(3)主程序

该程序存放在文件read-records.s中。

.include "linux.s"

.include "record-def.s"

.section .data

file_name: .ascii "test.dat\0"

.section .bss

.lcomm record_buffer, RECORD_SIZE

.section .text

.globl _start   #主程序

_start:    #定义存储输入输出描述符的栈位置

#也可用一个.data段中的内存地址代替

.equ ST_INPUT_DESCIPTOR, -4

.equ ST_OUTPUT_DESCRIPTOR, -8

movl %esp, %ebp

subl $8, %esp

#打开文件

movl $SYS_OPEN, %eax

movl $file_name, %ebx

movl $0, %ecx         #表示只读打开

movl $0666, %edx

int $LINUX_SYSCALL

movl %eax, ST_INPUT_DESCRIPTOR(%ebp)     #保存输入文件描述符

movl  $STDOUT, ST_OUTPUT_DESCRIPTOR(%ebp) #保存输出文件描述符,此处为“标准输出”

record_loop:    pushl ST_INPUT_DESCRIPTOR(%ebp)   #输入文件描述符

pushl $record_buffer                 #缓冲区地址指针

call read_record

addl $8, %esp

#比较读取的字节数和缓冲区大小

#如果不一致,说明达到文件尾部或出错

cmpl $RECORD_SIZE, %eax

jne finished_reading

#打印名,需先知道“名”的大小

pushl $RECORD_FIRSTNAME + record_buffer #该指令将两个常数相加,得到的结果为内存地址

#得到的结果将入栈。RECORD_FIRSTNAME记录从

#起始地址到名字字段之间的字节数

call count_chars

addl $4, %esp

movl %eax, %edx

movl ST_OUTPUT_DESCRIPTOR(%ebp), %ebx

movl $SYS_WRITE, %eax

movl $RECORD_FIRSTNAME + record_buffer, %ecx

int $LINUX_SYSCALL

jmp recor_loop

finish_reading:    movl $SYS_EXIT, %eax

movl $0, %ebx

int $LINUX_SYSCALL

编译、链接、执行上面程序

as read-record.s -o read-record.o

as count-chars.x -o count-chars.o

as write-newline.s -o write-newline.o

as read-records.s -o read-records.o

ld read-record.o count-chars.o write-newline.o read-records.o -o read-records

6、修改记录

修改程序主要有以下步骤:

1、打开输入文件和输出文件

2、从输入文件读取记录

3、递增年龄

4、新纪录写入文件

将下面程序存入文件add-year.s中

.include "linux.s"

.include "record-def.s"

.section .data

input_file_name: .ascii "test.dat\0"

output_file_name: .ascii "testout.dat\0"

.section .bss

.lcomm record_buffer, RECORD_SIZE

#局部变量的栈偏移量

.equ ST_INPUT_DESCRIPTOR, -4

.equ ST_OUTPUT_DESCRIPTOR, -8

.section .text

.globl _start

_start:    movl %esp, %ebp

subl $8, %esp

#打开输入文件

movl $SYS_OPEN, %eax

movl $input_file_name, %ebx

movl $0, %ecx

movl $0666, %edx

int $LINUX_SYSCALL

movl %eax, ST_INPUT_DESCRIPTOR(%ebp)   #输入文件的文件描述符

#打开输出文件

movl %SYS_OPEN, %eax

movl $output_file_name, %ebx

movl $0101, %ecx

movl $0666, %edx

int $LINUX_SYSCALL

movl %eax,ST_OUTPUT_DESCRIPTOR(%ebp)

loop_begin:    pushl ST_INPUT_DESCRIPTOR(%ebp)

pushl $record_buffer

call read_record

addl $8, %esp

#判断是否到达文件尾部,或出现读错误

cmpl $RECORD_SIZE, %eax

jne loop_end

#递增年龄

incl record_buffer + RECORD_AGE  #注意此处为直接寻址,和上面的“pushl $a+b”形式区别

#写记录

pushl ST_OUTPUT_DESCRIPTOR(%ebp)

pushl $record_buffer

call write_record

addl $8, %esp

jmp loop_begin

loop_end:    movl $SYS_EXIT, %eax

movl $0, %ebx

int $LINUX_SYSCALL

编译、链接、运行上面程序

as add-year.s -o add-years.o

ld add-year.o read-record.o write-record.o -o add-years

./add-years

7、让程序更加健壮

在上面add-years.s程序的基础上,我们添加下面这个程序。该程序打印错误消息并退出,其作为“被调函数”,由add-year.s程序调用。该函数存于文件:error-exit.s。为了使用该函数,需将错误信息的地址和错误代码入栈,然后进行调用。

.include "linux.s"

.equ ST_ERROR_CODE, 8

.equ ST_ERROR-MSG, 12

.globl .error_exit

.type error_exit, @function

error_exit:    pushl %ebp

movl %esp, %ebp

#写错误代码

movl ST_ERROR_CODE(%ebp), %ecx

pushl %ecx

call count_chars    #计算字符数

popl %ecx

movl %eax, %edx    #字符数量

movl $STDERR, %ebx

movl $SYS_WRITE, %eax

int $LINUX_SYSCALL

#写错误信息

movl STD_ERROR_MSG(%ebp), %ecx

pushl %ecx

call count_chars

popl %ecx

movl %eax, %edx

movl $SYS_WRITE, %eax

movl $STDERR, %ebx

int $LINUX_SYSCALL

pushl $STDERR

call write_newlines

#退出

movl $SYS_EXIT, %eax

movl $1, %ebx

int $LINUX_SYSCALL

以上为处理出错时的函数。下面将给出add-years.s是如何使用该函数的。

前面的add-year.s在打开文件后并未进行检错——可能出现找不到文件等情况。所以下面程序对打开文件后的返回码进行检测,保证正确打开文件。

#打开供读取的文件

movl $SYS_OPEN, %eax

movl $input_file_name, $ebx

movl $0, %ecx

movl $0666, %edx

int $LINUX_SYSCALL

movl %eax, INPUT_DESCRIPTOR(%ebp)    #将返回码存入栈中,供检测

#以下将对%eax存储的返回码进行检测,若为负值,则调用错误函数

cmpl $0, %eax

jl continue_processing   #0

#发送错误消息

.section .data

no_open_file_code:    .ascii "0001:\0"

no_open_file_msg:     .ascii "cant open input file\0"

.section .text

pushl $no_open_file_msg

pushl $no_open_file_code

call error_exit

continue_processing:

#程序其余部分

编译、链接、运行程序:

as add-year.s -o add-year.o

as error-exit.s -o error-exit.o

ld add-year.o write-newline.o error-exit.o read-record.o write-record.o count-chars.o -o add-year

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值