清除输入缓冲区部件x86NASM

如何解决清除输入缓冲区部件x86NASM

编辑:与此类似:Reset a string variable to print multitple user inputs in a loop (NASM Assembly)。但这不是同一个问题。

从另一篇文章中,我能够防止打印其他字符。但是,当程序返回到要求用户输入的位置时,我仍然无法阻止读取那些其他字符。


我正在创建一个程序,要求用户进行输入,然后将其打印出来。然后,它询问用户是否要打印其他文本,或按其他任何键以关闭程序,输入“ y”。

我的问题是,如果用户输入的字符比预期的多,这些多余的字符不会消失,并且当程序返回要求用户输入时,由于程序会占用其余的字符,因此没有机会输入输入上一次收到输入的字符。

例如:

要求用户输入要打印的文本,然后他们输入:“ Heyyyyyyyyyyyyyyyyyyyy yyy”

剩余物是“ yyy”

这时,程序应要求用户输入“ y”以重复该过程,或通过其他任何方式关闭程序。

输出:

  • Heyyyyyyyyyyyyyyyyyyyyyy

  • 想再试一次吗?如果是,请输入y。如果没有,请输入其他内容以关闭程序

  • 输入您的文本:

输出:yy

  • 想再试一次吗?如果是,请输入y。如果没有,请输入其他任何内容以关闭程序。

只有现在,它再次要求用户输入

由于“ yyy”仍在缓冲区中,因此在这种情况下,用户没有机会实际输入输入。

我该怎么做才能解决此问题?我最近才刚刚开始了解这一点,因此,感谢您的帮助。

谢谢。

这就是我所做的

prompt db "Type your text here.",0h
retry db "Wanna try again? If yes,enter y. If not,enter anything else to close the program"

section .bss
text resb 50
choice resb 2

section .text
    global _start

_start:

    mov rax,1      ;Just asking the user to enter input
    mov rdi,1
    mov rsi,prompt
    mov rdx,21
    syscall


    mov rax,0      ;Getting input and saving it on var text
    mov rdi,0
    mov rsi,text
    mov rdx,50
    syscall

    mov r8,rax     ;This is what I added to prevent additional characters
                    ;from being printed

    mov rax,1      ;Printing the user input
    mov rdi,r8
    syscall

    mov rax,1      ;Asking if user wants to try again
    mov rdi,retry
    mov rdx,83
    syscall

    mov rax,0      ;Getting input and saving it on var choice
    mov rdi,choice
    mov rdx,2
    syscall

    mov r8b,[choice] ;If choice is different from y,go to end and close the program. Otherwhise,go back to start.
    cmp byte r8b,'y'
    jne end
    jmp _start

    end:
    mov rax,60      
    mov rdi,0       
    syscall

解决方法

清除stdin的简单方法是检查choice中的第二个字符是否为'\n'0xa)。如果不是,则字符保留在stdin中,未读。您已经知道如何从stdin进行读取,因此在这种情况下,只需读取stdin,直到读取'\n',例如

1 。

    mov rax,0      ;Getting input and saving it on var choice
    mov rdi,0
    mov rsi,choice
    mov rdx,2
    syscall

    cmp byte [choice],'y'  ; check 1st byte of choice,no need for r8b
    jne end
    
    cmp byte [choice + 1],0xa  ; is 2nd char '\n' (if yes done,jump start)
    je _start
    
    empty:          ; chars remain in stdin unread
    mov rax,0      ; read 1-char from stdin into choice
    mov rdi,1
    syscall
    
    cmp byte [choice],0xa  ; check if char '\n'?
    jne empty               ; if not,repeat
    
    jmp _start

除此之外,您还应该在声明时确定提示长度,例如

prompt db "Type your text here. ",0h
plen equ $-prompt
retry db "Try again (y/n)? ",0h
rlen equ $-retry

这样,您就不必对长度进行硬编码,例如,在更改提示时

_start:

    mov rax,1      ;Just asking the user to enter input
    mov rdi,1
    mov rsi,prompt
    mov rdx,plen
    syscall


    mov rax,0      ;Getting input and saving it on var text
    mov rdi,text
    mov rdx,50
    syscall

    mov r8,rax

    mov rax,1      ;Printing the user input
    mov rdi,r8
    syscall

    mov rax,1      ;Asking if user wants to try again
    mov rdi,retry
    mov rdx,rlen
    syscall

如果完全放在一起,则可以执行以下操作:

prompt db "Type your text here. ",0h
rlen equ $-retry

section .bss
text resb 50
choice resb 2

section .text
    global _start

_start:

    mov rax,rlen
    syscall

    mov rax,repeat
    
    jmp _start

    end:
    mov rax,60      
    mov rdi,0       
    syscall

使用/输出示例

$ ./bin/emptystdin
Type your text here. abc
abc
Try again (y/n)? y
Type your text here. def
def
Try again (y/n)? yes please!
Type your text here. geh
geh
Try again (y/n)? yyyyyyyyyyyyyeeeeeeeeeesssssssss!!!!
Type your text here. ijk
ijk
Try again (y/n)? n

现在,即使在(y/n)?提示符下踩猫的键盘也不会造成问题。用syscall可能有更优雅的方式来处理此问题,该方法比重复读取更有效,但这将解决此问题。


其他注意事项和错误检查

如上所述,一次简单地读取和检查字符不是一种非常有效的方法,尽管从概念上讲,这是最简单的扩展,无需进行其他更改。 @PeterCordes在下面的评论中指出了许多优点,这些观点与更有效的方法有关,更重要的是对于可能出现的错误情况也应加以防范。

对于初学者,当您需要查找有关单个系统调用用途的信息时,Anatomy of a system call,part 1提供了一些有关如何使用它们的背景知识,并在Linux手册页中进行了补充,有关详细信息,请阅读man 2 read。参数类型以及返回类型和值。

上面的原始解决方案无法解决如果用户通过按 Ctrl + d 生成手动的文件结尾或实际发生读取错误的情况。它只是解决了用户输入和清空stdin问题。对于任何用户输入,在使用该值之前,必须通过检查返回值来验证输入是否成功。 (不仅是/否输入,还包括所有输入)。为此,您可以将零输入(手动文件结尾)或负返回(读取错误)视为失败输入。

要检查您是否有至少一个有效的输入字符,只需检查return(read返回读取的字符数,sys_read将该值放在rax之后syscall)。零或负值表示未接收到输入。支票可能是:

    cmp rax,0              ; check for 0 bytes read or error
    jle error

您可以向用户编写简短的诊断信息,然后根据需要处理错误,此示例仅在输出诊断信息后退出,例如

readerr db 0xa,"eof or read error",0xa,0x0
rderrsz equ $-readerr
...
    ; your call to read here
    cmp rax,0              ; check for 0 bytes read or error
    jle error
    ...
    error:
    mov rax,1              ; output the readerr string and jmp to end
    mov rdi,readerr
    mov rdx,rderrsz
    syscall
    
    jmp end

现在继续以更有效的方式清空stdin。原始答案中最大的障碍表明,重复使用系统调用sys_read以一次重用2字节choice缓冲区来一次读取一个字符。显而易见的解决方案是使choice更大,或者每次仅使用堆栈空间读取更多字符。 (您可以查看几种方法的注释)在这里,例如,我们将choice增加到128字节,在预期的"y\n"输入的情况下,将仅使用其中的两个个字节,但是如果输入的时间过长,则一次将读取128个字节,直到找到'\n'。要进行设置,您需要:

choicesz equ 128
...
section .bss
text resb 50
choice resb 128

现在,在您要求(y/n)?之后,您的读物将会是:

    mov rax,0              ; Getting input and saving it on var choice
    mov rdi,choicesz
    syscall

    cmp rax,0              ; check for 0 bytes read (eof) or error
    jle error
    
    cmp byte [choice],no need for r8b
    jne end                 ; not a 'y',doesn't matter what's in stdin,end

现在有两个条件需要检查。首先,将读取的字符数与缓冲区大小choicesz进行比较,如果读取的字符数小于choicesz,则stdin中不会有未读取的字符。其次,如果返回值等于缓冲区大小,则stdin中可能剩余字符,也可能没有字符。您需要检查缓冲区中的最后一个字符以查看它是否为'\n',以指示您是否已读取所有输入。如果最后一个字符不是'\n'字符以外的字符仍未读取(除非用户刚巧在第128个字符上生成了手​​动的文件结尾),您可以检查为:>

    empty:
    cmp eax,choicesz       ; compare chars read and buffer size
    jb _start               ; buffer not full - nothing remains in stdin
    
    cmp byte [choice + choicesz - 1],0xa   ; if full - check if last byte \n,done
    je _start
    
    mov rax,0              ; fill choice again from stdin and repeat checks
    mov rdi,choicesz
    syscall
    
    cmp rax,0              ; check for 0 bytes read or error
    jle error

    jmp empty

注意:,如上所述,还有另外一种情况需要涵盖,在此未涵盖,例如用户输入有效输入,然后生成手动-文件,而不仅仅是在第128个字符(或128的倍数)后按 Enter 。在那里,您不能只是查找不存在的'\n',并且如果没有更多的chacters并再次调用sys_read,它将阻止输入的等待。可以想象,您将需要使用非阻塞读取和回退单个字符来打破这种歧义-这留给您)

具有改进效果的完整示例为:

prompt db "Type your text here. ",0x0
plen equ $-prompt
retry db "Try again (y/n)? ",0x0
rlen equ $-retry
textsz equ 50
choicesz equ 128
readerr db 0xa,0x0
rderrsz equ $-readerr

section .bss
text resb 50
choice resb 128

section .text
    global _start

_start:

    mov rax,1              ; Just asking the user to enter input
    mov rdi,plen
    syscall

    mov rax,0              ; Getting input and saving it on var text
    mov rdi,textsz
    syscall
    
    cmp rax,0              ; check for 0 bytes read or error
    jle error

    mov r8,1              ; Printing the user input
    mov rdi,1              ; Asking if user wants to try again
    mov rdi,0              ; check for 0 bytes read (eof) or error
    jle error

    cmp byte [choice],end
    
    empty:
    cmp eax,0              ; check for 0 bytes read or error
    jle error

    jmp empty
    
    end:
    mov rax,0       
    syscall
    
    error:
    mov rax,rderrsz
    syscall
    
    jmp end

肯定有更有效的方法来优化此方法,但是出于讨论“如何清空stdin?”的目的,使用缓冲区大小的第二种方法可以使对sys_read的重复调用成为可能。一次读取一个字符是向前迈出的重要一步。 “如何完全优化支票?”是一个完全独立的问题。

如果您还有其他问题,请告诉我。

脚注:

1。。在这种情况下,用户输入内容时,用户可以通过按 Enter 来生成'\n',允许您检查{{ 1}}作为清空'\n'的最后一个字符。用户还可以通过按 Ctrl + d 来生成手动的文件结尾,因此不能保证stdin。还有许多其他方法可以填充'\n',例如将文件重定向为输入,其中应该有结尾stdin以符合POSIX,但这也不能保证。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-