【问题标题】:Intermediate command-line interfaces [closed]中间命令行界面[关闭]
【发布时间】:2011-10-25 22:09:49
【问题描述】:

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

这种界面是我应该使用curses 的,还是中间有一个步骤?最好是跨平台的。

【问题讨论】:

  • 你为什么不看一下wget的源代码,看看下载米/进度条是如何工作的?它的开源:你可以从中学习。
  • @derobert 好主意...我没想到。

标签: c io ncurses


【解决方案1】:

您可以通过打印退格字符'\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");

请注意,只有在写入实际终端时才应该这样做(而不是被重定向到文件或管道)。您可以使用if(isatty(fileno(stdout)) { ... } 进行测试。当然,如果您使用任何其他库(例如 curses 或 ncurses)也是如此。

【讨论】:

  • +1。顺便说一句,建议在执行此操作之前检查isatty(fileno(stdin))
  • @larsmans:是的,很好(除非您想测试stdout 是否是终端,而不是stdin)。
  • 嗯,stdout,是的。傻我:)
  • 我从所有这些回复中猜测不会有真正的跨平台解决方案(鉴于 isatty 似乎在 CS2005 中已被弃用),但谢谢!
  • 如果用户按下回车键,或者更重要的是,如果您的进度消息长度缩短,您的代码会出现有趣的问题。可以使用 terminfo 构建更强大的解决方案。
【解决方案2】:

stdiocurses 之间是标准的 Unix/POSIX termios 库。关闭字符回显并读取一行的简单示例程序(注意,没有任何错误检查):

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

void munch_line()
{
    int c;
    while ((c = getchar()) != EOF && c != '\n')
        ;
}

int main()
{
    int fd;
    struct termios tio;

    printf("Enter something: ");
    tcgetattr(fileno(stdin), &tio);
    tio.c_lflag &= ~ECHO;
    tcsetattr(fileno(stdin), TCSANOW, &tio);

    munch_line();
    putchar('\n');
}

在退出程序之前不要忘记重新打开回显;)

【讨论】:

    【解决方案3】:

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

    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):

    #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>
    

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

    【讨论】:

      【解决方案4】:

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

      xiv 视为增量计数器,将size_of_pages 视为一次输入或输出的数据量。它们都是 uint64_t (有点不必要)。 totalfloat。它具有要读取的整个 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);
      }
      

      【讨论】:

        猜你喜欢
        • 2016-03-12
        • 2021-06-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-06
        • 2014-10-16
        相关资源
        最近更新 更多