C语言中如何获取终端输入字符数并准确定位光标?
问题解决:非阻塞输入时保持光标在输入内容末尾
原代码核心问题
- ioctl目标错误:用
stdout(1)调用FIONREAD,应该用stdin(0)来查询输入缓冲区的待读取字节数。 - 光标位置固定:定时器触发后强制将光标移到输入起始列(15),没有叠加已输入的字符长度。
- 未处理fgets被信号打断:
alarm触发的SIGALRM会打断fgets,此时fgets返回NULL且errno=EINTR,原代码未处理会直接退出输入循环。
修改后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sys/ioctl.h> #include <errno.h> int time_left = 25; // 定义输入起始列,方便后续统一计算 #define INPUT_START_COL 15 void moveTo(int row, int col) { printf("\x1b[%d;%df", row, col); fflush(stdout); } void update_timer(int signum) { int bytes_waiting = 0; // 改用stdin的文件描述符0查询输入缓冲区字节数 int res = ioctl(0, FIONREAD, &bytes_waiting); if (res == -1) { perror("ioctl"); exit(1); } if (time_left >= 0) { // 更新定时器显示 moveTo(0, 15); if (time_left < 10) printf("%d ", time_left); else printf("%d", time_left); fflush(stdout); // 更新已输入字符数显示 moveTo(3, 15); printf("res: %d, bytes: %d", res, bytes_waiting); fflush(stdout); // 动态计算光标位置:起始列 + 已输入字节数 moveTo(2, INPUT_START_COL + bytes_waiting); time_left -= 1; if (time_left >= 0) alarm(1); } } int main() { // 初始化界面布局 moveTo(0, 7); printf("Time: "); moveTo(2, 7); printf("Input: "); moveTo(3, 7); printf("Chars: "); moveTo(2, INPUT_START_COL); fflush(stdout); // 设置信号处理函数 signal(SIGALRM, update_timer); alarm(1); char input[100]; while (time_left > 0) { errno = 0; // 处理fgets被SIGALRM打断的情况 if (fgets(input, sizeof(input), stdin)) { moveTo(10, 10); printf("You entered: %s", input); fflush(stdout); // 输入完成后,光标回到输入起始位置 moveTo(2, INPUT_START_COL); fflush(stdout); } else if (errno != EINTR) { // 非信号打断的错误,退出循环 break; } } return 0; }
关键修改说明
- ioctl目标修正:用
ioctl(0, FIONREAD, &bytes_waiting)获取输入缓冲区中未读取的字节数,这就是用户当前已输入但未回车的字符数。 - 光标位置动态计算:定时器触发时,将光标移到
INPUT_START_COL + bytes_waiting位置,确保光标停在已输入内容的末尾。 - 处理fgets被打断:当
fgets因SIGALRM返回NULL时,检查errno是否为EINTR,若是则重新调用fgets,避免输入循环意外退出。 - 界面刷新优化:所有光标移动和打印操作后立即调用
fflush(stdout),确保终端界面实时更新。
内容的提问来源于stack exchange,提问作者Code200monkey
相关产品推荐
相关产品推荐

