You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中如何获取终端输入字符数并准确定位光标?

问题解决:非阻塞输入时保持光标在输入内容末尾

原代码核心问题

  1. ioctl目标错误:用stdout(1)调用FIONREAD,应该用stdin(0)来查询输入缓冲区的待读取字节数。
  2. 光标位置固定:定时器触发后强制将光标移到输入起始列(15),没有叠加已输入的字符长度。
  3. 未处理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 19:10:34