C 中间命令行接口

C 中间命令行接口,c,io,ncurses,C,Io,Ncurses,我使用C已经有一段时间了,我相当精通简单的命令行界面。我还玩过curses库,用于终端应用程序,它的功能不仅仅是向stdout写入文本。然而,我不知道中间点在哪里——例如,像wget或make这样的应用程序能够更新它们输出的文本(比如wget的弹跳下载表和进度条),而不占用整个屏幕 这种界面是我应该用来诅咒的,还是介于两者之间?最好是跨平台的。只需打印退格字符'\b'和原始回车'\r'(无换行符),即可完成一些简单的操作。退格将光标向后移动一个字符,允许您覆盖输出;回车将光标移回当前行的开头,允

我使用C已经有一段时间了,我相当精通简单的命令行界面。我还玩过
curses
库,用于终端应用程序,它的功能不仅仅是向
stdout
写入文本。然而,我不知道中间点在哪里——例如,像
wget
make
这样的应用程序能够更新它们输出的文本(比如
wget
的弹跳下载表和进度条),而不占用整个屏幕


这种界面是我应该用来诅咒的,还是介于两者之间?最好是跨平台的。

只需打印退格字符
'\b'
和原始回车
'\r'
(无换行符),即可完成一些简单的操作。退格将光标向后移动一个字符,允许您覆盖输出;回车将光标移回当前行的开头,允许您覆盖当前行

下面是一个简单的进度条示例:

int progress = 0;
while(progress < 100)
{
    // Note the carriage return at the start of the string and the lack of a
    // newline
    printf("\rProgress: %d%%", progress);
    fflush(stdout);

    // Do some work, and compute the new progress (0-100)
    progress = do_some_work();
}
printf("\nDone\n");
int progress=0;
而(进度<100)
{
//注意字符串开头的回车符和缺少空格
//新线
printf(“\r进度:%d%%”,进度);
fflush(stdout);
//做一些工作,计算新的进度(0-100)
进度=做一些工作();
}
printf(“\nDone\n”);

请注意,只有在向实际终端写入数据时才应执行此操作(而不是重定向到文件或管道)。您可以使用
if(isatty(fileno(stdout)){…}
来测试这一点。当然,如果您使用任何其他库,如curses或ncurses,也会如此。

介于
stdio
curses
之间的是标准的Unix/POSIX库。关闭字符回音并读取一行的简单示例程序(注意,无任何错误检查):

#包括
#包括
#包括
void munch_line()
{
INTC;
而((c=getchar())!=EOF&&c!='\n')
;
}
int main()
{
int-fd;
结构术语;
printf(“输入某物:”);
tcgetattr(文件号(标准输入)和tio);
tio.Clflag&=~ECHO;
tcsetattr(文件号(标准输入)、TCSANOW和tio);
咀嚼线();
putchar('\n');
}
在退出程序之前,不要忘记重新打开回显;)

如果您的终端支持,您可以使用它们来移动光标:

printf("\x1b[%uD", n);  /* move cursor n steps to the left */
printf("\x1b[%uC", n);  /* move cursor n steps to the right */
printf("\x1b[K");       /* clear line from cursor to the right */
printf("\x1b[1K");      /* clear line from cursor to the left */
printf("\x1b[2K");      /* clear entire line */
一个简单的例子(
curtest.c
):


Windows命令行没有内置的VT100支持。

在这里,您可以看到一些熟悉这些答案主题的内容。我们中的一些人正在使用简单的回车,我也是

考虑到
xiv
是一个增量计数器,
size\u of\u pages
一次显示用于输入或输出的数据量。它们都是uint64\u t(有些不必要)。
total
是一个
float
。它有整个IO的长度可以读取

if ((((float)(xiv * size_of_pages)/total)*100) >= xnt)
{
    xnt = 0.01 + (((float)(xiv * size_of_pages)/total)*100);
    printf("\rPercent Done: %.2f%% [", xnt);
    float vbc = 0;
    kp = 0;
    while (vbc < 10)
    {
        if (kp < (xnt)/10) {
            printf("#");
            kp += 1;
        }
        else
        {
            printf(":");
        }
        vbc++;
    }
    cout << "]" << flush;
    fflush(stdout);
}
if(((浮动)(xiv*size\u/u页)/总计)*100)>=xnt)
{
xnt=0.01+((浮动)(xiv*页数/总页数)*100);
printf(“\r完成百分比:%.2f%%[”,xnt);
浮动vbc=0;
kp=0;
而(vbc<10)
{
如果(kp<(新台币)/10){
printf(“#”);
kp+=1;
}
其他的
{
printf(“:”);
}
vbc++;
}

你为什么不看看wget的源代码,看看下载表/进度条是如何工作的呢?它的开源:你可以从中学习。@derobert好主意…我没想到。+1.在做这件事之前,最好先检查
isatty(fileno(stdin))
。@larsmans:是的,说得好(除非你想测试stdout是否是一个终端,而不是stdin)。嗯,
stdout
,是的。愚蠢的我:)我猜从所有这些回答中不会有真正的跨平台解决方案(考虑到CS2005中
isatty
),但谢谢!如果用户按enter键,或者更重要的是,如果进度消息长度缩短,则代码会出现有趣的问题。可以使用terminfo构建更健壮的解决方案。
#include <stdio.h>

int main(void)
{
    printf("ZZZZZZZZZZ");
    printf("\x1b[%dD", 10U);  /* move cursor 10 steps to the left */
    printf("YYYYYYYYY");
    printf("\x1b[%dD", 9U);   /* move cursor 9 steps to the left */
    printf("XXXXXXXX");
    printf("\x1b[%dD", 2U);   /* move cursor 2 steps to the left */
    printf("\x1b[1K");       /* clear line from cursor to the left */
    printf("\r\n");
    return 0;
}
mizo@host:~/test> gcc curtest.c 
mizo@host:~/test> ./a.out
       XYZ
mizo@host:~/test>
if ((((float)(xiv * size_of_pages)/total)*100) >= xnt)
{
    xnt = 0.01 + (((float)(xiv * size_of_pages)/total)*100);
    printf("\rPercent Done: %.2f%% [", xnt);
    float vbc = 0;
    kp = 0;
    while (vbc < 10)
    {
        if (kp < (xnt)/10) {
            printf("#");
            kp += 1;
        }
        else
        {
            printf(":");
        }
        vbc++;
    }
    cout << "]" << flush;
    fflush(stdout);
}