如何解决清除输入缓冲区部件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 举报,一经查实,本站将立刻删除。