擦除显示的字符

Erase the displayed characters

我正在做“C 编程语言”第 2 版的练习,作者是 Brian W. Kernighan 和 Dennis M. Ritchie。

练习 1.9 让我很困惑。所以这里是:

"Write a program to copy its input to output, replacing each string of one more blanks by a single blank".

我对此感到困惑。我 post 我的问题在这里,因为其他学习者没有像我想做的那样做这个练习。在这里,用-表示一个空格:

Input: ----hello
Output: -hello

但我想要这个:

Input: ----hello
Output: ----hello
Input: ----
Output: -

这是我的程序:

#include <stdio.h>
#include <stdlib.h>

int main()
{
 int character; /* input character */
 int non_blank = 0; /* is any non blank character */

 while ((character = getchar()) != EOF) {
    if (character != '\n') {
      putchar(character);
      if (character != ' ') {
          non_blank = 1;
      }
    }
    else {
      if (non_blank == 0 ) {
        putchar('\b'); /* go to previous line (blabla\n|cursor|) (blabla|cursor|\n) */
        putchar('\r'); /* carriage return */
        putchar(' '); /* put one space */
        putchar('[=12=]'); /* and put the end of line */
      }

      non_blank = 0;
    }
 }

 return EXIT_SUCCESS;
}

但空格不会被删除。

Input: ------|\n
Output: -|----- 

这是我对转义序列的实验:

#include <stdio.h>
#include <stdlib.h>

int main()
{
  printf("bbbbbbbbbbbbbbb");

  putchar('\b'); /* jump to previous line */
  putchar('\r');
  putchar('b');
  putchar('[=14=]');
  return EXIT_SUCCESS;
}  

|是游标。

Output: b|

但是当我添加换行符时,它不起作用。如何使其与换行符一起使用?

所需的算法应类似于:

implement a state machine with two states.  NotInSpaces and InSpaces
(with just two states, a boolean would work)
no special treatment for '\n'

set state to NotInSpaces
Loop: (exit loop on EOF)
    getchar
    if char is space 
    then if state is NotInSpaces
         then set state to InSpaces
              output space
         endif
    else
         output char
    endif
end Loop:
output '\n' // to force output buffer flush

您的代码的问题在于,您在处理字符后立即打印字符,并且在检测到必须将它们压缩为一个字符后尝试擦除它们 space。

你的方法是有效的,但你必须首先知道 \b 只会让光标返回一个 space (没有擦除),所以正确的方法是发出 \b \b(一个后退space,一个space覆盖字符,另一个后退space将光标留在正确的位置)

另一种方法是等待打印任何内容,直到我们知道该行是以非空字符串结尾还是仅以 \n 字符结束。

以下代码对空白字符进行计数并管理两种状态来应对这种情况。当然,您将能够遵循代码,我已尝试用注释对其进行记录。

#include <stdio.h>
int main()
{
    int c;
    int state = 0;
    int nblanks = 0;

    /* read chars up to EOF */
    while((c = getchar()) != EOF) {
        switch (state) {
        case 0: /* no nonblank character read */
            switch (c) {

            /* accumulate number of blanks to be printed in case
             * we see a nonblank char before \n */
            case ' ': nblanks++; break;

            /* in this case we have seen nblanks before a \n, so
             * we have to print only one blank and \n */
            case '\n':
                      puts(" "); /* just one space */
                      nblanks = 0; /* reset counter */
                      break;

            /* nonblank char */
            default:
                      /* print all the blanks up to here */
                      printf("%*s%c", nblanks, "", c);
                      state = 1; /* switch state */
                      break;
            } /* switch */
            break;

        /* we have read at least a nonblank char, so we must
         * echo the line until the end. */
        case 1: 
            if (c == '\n') {
                state = 0;
                nblanks = 0;
            } /* if */
            putchar(c);
            break;
        } /* switch */
    } /* while */
} /* main */

完全兼容K&R第二版,只需要编译即可运行。

也许最难理解的语句是中间的 printf() 行。星号 * 允许我使用 printf 参数指定字段的宽度,传递空字符串 "" 允许我打印带有 nblanks spaces 的字符串。最后的 %c 格式是打印刚刚读取的非空字符。这导致了紧凑而高效的代码。

最后,你的一些错误想法:

  • \b 仅返回终端中的一个字符(一个字符,而不是一行)。我不仅在打印到终端或打印机时有效。它不适用于文件(文件将返回 spaces 作为内部的普通字符)
  • \r 使光标​​移动到第一个显示列。这仅适用于屏幕和打印机。它在文件中不起作用,它作为普通字符包含在内。
  • [=21=] 是 C 程序用来结束字符串的字符(知道它们在内存中的什么位置结束)它不是输出设备的特殊字符,它什么都不做(它是 ASCII NUL 字符,在输出端什么也不做)
  • 最后,如果您尝试在打印机上使用 \b \b 方法,它不会擦除任何内容,因为 space 不会擦除已经打印的内容。那么,就得采用第二种方法了。