【问题标题】:Erase the displayed characters擦除显示的字符
【发布时间】:2015-03-29 15:48:47
【问题描述】:

我正在用 Brian W. Kernighan 和 Dennis M. Ritchie 编写的“C 编程语言”第二版进行练习。

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

“编写一个程序将其输入复制到输出,用一个空格替换每个空格的字符串”。

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

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('\0'); /* 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('\0');
  return EXIT_SUCCESS;
}  

| 是一个游标。

Output: b|

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

【问题讨论】:

  • 你尝试解决这个练习还是自己发明?
  • 你确定你理解这个问题吗?我相信练习是要求您用单个空格替换大于 1 的空格序列,因此“test...test”变为“test.test”,其中“.”是空间的替身。您的“我想要这个”部分似乎不同意。
  • 当我第一次阅读本书俄语版的练习时,我意识到我应该将每个只包含空格的字符串替换为一个空格。当我用英文阅读练习时,我也意识到了这一点。你能帮我解释一下运动吗?
  • 许多操作系统中的 '\r' 会导致光标移动到下一行,而不仅仅是当前行的开头。您可以使用转义序列将光标移动到上一行的开头,但它们不适合本练习。
  • 感谢大家的帮助。并特别感谢帮助使问题更清晰的编辑。

标签: c escaping kernighan-and-ritchie


【解决方案1】:

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

您的方法是有效的,但您必须首先知道 \b 只会使光标返回一个空格(不擦除),因此正确的方法是发出 \b \b (一个退格,一个要覆盖的空格字符和另一个退格键将光标留在正确的位置)

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

以下代码计算空白字符并管理两种状态来应对。当然,您将能够按照代码进行操作,并且我已尝试使用 cmets 对其进行记录。

#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 空格的字符串。最终的%c 格式是打印刚刚读取的非空白字符。这会导致代码紧凑而高效。

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

  • \b 只返回终端中的一个字符(一个字符,而不是一行)。我不仅在打印到终端或打印机时工作。它不适用于文件(文件将退格作为普通字符在里面)
  • \r 使光标转到第一个显示列。这仅适用于屏幕和打印机。它在文件中不起作用,它作为普通字符包含在内。
  • \0 是 C 程序用来完成字符串的字符(知道它们在内存中的什么位置完成) 它不是输出设备的特殊字符,它什么也不做(它是 ASCII NUL 字符,在输出终端)
  • 最后,如果您尝试在打印机上遵循\b \b 方法,它不会擦除任何内容,因为空格不会擦除已打印的内容。那么,你必须遵循第二种方法。

【讨论】:

  • 非常感谢!您的算法适合我的方法,无需使用转义序列。太棒了!
【解决方案2】:

所需的算法应该是这样的:

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

【讨论】:

  • 查看问题。这个算法不适合我的方法。但是感谢您的评论。
猜你喜欢
  • 2019-06-06
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
  • 2019-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-14
相关资源
最近更新 更多