MIPS(MARS环境)读取文本文件存储为字符串数组的问题
问题根因
Windows系统的文本文件用\r\n(ASCII码13、10)作为换行分隔符,而MARS的4号字符串打印系统调用,只会在遇到\0(ASCII码0,空终止符)时才停止输出。你当前代码只做了文件全量读取,没有把单词之间的换行符替换为空终止符,也没有拆分每个单词的起始地址,直接从缓冲区头部打印自然会把后续所有内容全部输出。
现有代码还存在两个参数错误:
- 预留的
inputBuffer空间是14976字节,但读文件时传入的最大读取长度是14946,参数不匹配 - 没有存储每个单词的起始地址,无法单独索引单个字符串
注意:MARS中14号读文件系统调用不会自动在读取内容末尾加空终止符,不做处理的话打印字符串会读到缓冲区外的垃圾值
修复方案
- 读完文件后遍历整个输入缓冲区,把所有
\r、\n字符替换为\0,实现每个单词的空终止符分隔 - 额外预留一块指针数组空间,遍历过程中记录每个单词的起始地址存入数组,后续要访问第n个单词,直接取数组中第n项的地址即可
- 修正读文件的长度参数,和预留缓冲区大小对齐,避免读溢出
修正后可运行代码
.data dictionary: .asciiz "dictionary.txt" inputBuffer: .space 14976 # 预留2496个单词的存储空间 wordArray: .space 9984 # 单词指针数组,每个地址占4字节,共可存2496个单词地址 .text openDictionary: # 打开字典文件 la $a0, dictionary addi $a1, $zero, 0 addi $a2, $zero, 0 li $v0, 13 syscall move $t0, $v0 # 保存文件描述符 # 读取文件内容到缓冲区 li $v0, 14 move $a0, $t0 la $a1, inputBuffer addi $a2, $zero, 14976 # 读取长度和缓冲区大小对齐 syscall move $t1, $v0 # 保存实际读取到的字节总数 # 关闭文件 li $v0, 16 move $a0, $t0 syscall # 遍历缓冲区:替换换行符为\0,同时构建单词指针数组 la $t0, inputBuffer # t0存当前遍历到的缓冲区地址 la $t2, wordArray # t2存当前指针数组的写入位置 addi $t3, $zero, 0 # t3存已处理的字节计数 addi $t5, $zero, 1 # t5为标记位:1表示下一个普通字符是新单词的开头 process_loop: beq $t3, $t1, process_end # 所有字节处理完成则退出循环 lb $t4, 0($t0) # 读取当前位置的字节 # 判断当前字节是否为换行符 beq $t4, 13, replace_newline beq $t4, 10, replace_newline # 当前是普通单词字符,如果是单词开头就把地址存入指针数组 beq $t5, $zero, next_char sw $t0, 0($t2) addi $t2, $t2, 4 addi $t5, $zero, 0 j next_char replace_newline: sb $zero, 0($t0) # 把换行符替换为空终止符 addi $t5, $zero, 1 # 标记下一个普通字符是新单词开头 next_char: addi $t0, $t0, 1 addi $t3, $t3, 1 j process_loop process_end: # 测试:打印第一个单词 li $v0, 4 la $t0, wordArray lw $a0, 0($t0) # 取第1个单词地址,要打印第n个单词则取偏移量为4*(n-1)的地址即可 syscall jr $ra
运行效果
运行上述代码后,只会输出第一个单词abed,不会连带输出后续内容。需要访问其他单词时,只需要修改指针数组的访问偏移量即可,比如访问第二个单词就用lw $a0, 4($t0),第三个用lw $a0, 8($t0),以此类推。
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

