无法理解带有特殊字符的C ++字符串的输出

Sha*_* Yu 7 c++ string special-characters

我正在使用带有特殊字符的c ++字符串进行控制台输出。大部分结果都是可以预测的,但是其中之一超出了我的预期。我在任何地方都找不到答案。

平台:Windows 7企业版6.1(内部版本7601:Service Pack 1)编译器:g ++(GCC)8.2.0,c ++ 17

#include <iostream>

int main(){
    using namespace std;
    char numString[12] = "0123456789\n";

    //This is group 1
    numString[3] = '\t';
    numString[4] = '\b';
    cout << "Group 1 output:\n" << numString << endl;

    //This is group 2
    numString[3] = '\b';
    numString[4] = '\t';
    cout << "Group 2 output:\n" << numString << endl;

    //This is group 3
    numString[3] = '\n';
    numString[4] = '\b';
    cout << "Group 3 output:\n" << numString << endl;

    //This is group 4
    numString[3] = '\b';
    numString[4] = '\n';
    cout << "Group 4 output:\n" << numString << endl;

    //This is group 5
    numString[2] = '\b';
    numString[3] = '\b';
    numString[4] = '\n';
    cout << "Group 5 output:\n" << numString << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

控制台中的输出:

第1组输出:
01256789

第2组输出:
01 56789

第3组输出:
012
56789

第4组输出:
012
56789

第5组输出:
01
56789

预计第四组输出为:

第4组输出:
01
56789

虽然输出实际上是

第4组输出:
012
56789

我不明白的是为什么字符“ 2”仍然存在。

任何人都可以帮助我理解问题吗?谢谢。


在看到下面的答案(尤其是zar的答案)之后,我相信我已经理解了这个问题,并且想在这里总结一下。

  1. 当未检测到物理按键时,Windows cmd控制台处于非破坏性模式
  2. 任何新的输出都会从当前光标开始覆盖现有的输出。听起来很多余,但有必要。如果有任何新字符输出,它将覆盖现有字符,直到用完新字符。如果还有更多现有字符,它们将继续存在于该位置,并且看起来像是新字符后面的“输出”。
  3. '\ b'将光标向后移动一个字符。它不会删除任何内容。
  4. '\ n'将光标移动到下一行。它不会在其后移动任何字符到下一行。
  5. '\ r'将光标移动到当前行的开头。

请注意移动光标

我想在这里粘贴所有代码:

//strwithspecialchar.cpp -- Understand special characters in C++ string
#include <iostream>

int main(){
    using namespace std;
    char numString[12] = "0123456789\n";

    //This is group 1
    numString[3] = '\t';
    numString[4] = '\b';
    cout << "Group 1 output:\n" << numString << endl;

    //This is group 2
    numString[3] = '\b';
    numString[4] = '\t';
    cout << "Group 2 output:\n" << numString << endl;

    //This is group 3
    numString[3] = '\n';
    numString[4] = '\b';
    cout << "Group 3 output:\n" << numString << endl;

    //This is group 4
    numString[3] = '\b';
    numString[4] = '\n';
    cout << "Group 4 output:\n" << numString << endl;

    //This is group 5
    numString[2] = '\b';
    numString[3] = '\b';
    numString[4] = '\n';
    cout << "Group 5 output:\n" << numString << endl;

    //This is group 6
    numString[2] = '\b';
    numString[3] = '3';
    numString[4] = '\n';
    cout << "Group 6 output:\n" << numString << endl;

    //This is group 7
    numString[2] = '2';
    numString[3] = '\b';
    numString[4] = '\a';
    cout << "Group 7 output:\n" << numString << endl;

    //This is group 8
    numString[3] = '\b';
    numString[4] = '\r';
    cout << "Group 8 output:\n" << numString << endl;

    //This is group 9
    numString[3] = '\b';
    numString[4] = '\n';
    numString[8] = '\r';
    cout << "Group 9 output:\n" << numString << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

下面的输出可以更好地理解这些特殊字符:

第1组输出:
01256789

第2组输出:
01 56789

第3组输出:
012
56789

第4组输出:
012
56789

第5组输出:
01
56789

第6组输出:
03
56789

第7组输出:
0156789

第8组输出:
56789

第9组输出:
012
967

Lig*_*ica 9

这取决于您的终端。我们可以很容易地从一个平台上看到,该平台不会'\b'以任何特殊的方式呈现控制字符,该控制字符出现在预期位置的字符串中

通过Coliru的一些“原始” ish输出的屏幕截图

那么,为什么不“擦除” 2呢?

如果我们打开的cmd.exe和类型ABCtrl+ H那么我们看到的B将立即删除。这似乎无法证明cmd.exe 像许多控制台一样 “无损地”处理退格的想法。

但这并不能反驳它!这似乎是对按键的一种特殊处理,大概与实际的退格字符的工作方式有关。毕竟,您希望退格字符实际上是擦除内容,而不是仅移动光标。

如果在不是由键盘生成的输出中找到cmd.exe,则以不同的方式对待控制字符:以一种非破坏性的方式。因此它将向后移动光标,然后下一个字符“覆盖”将要擦除的字符。

但是在第4组中,您有换行符,因此下一个字符在下一行上,并且不在正确的位置以擦除任何内容。

通过构造一个特殊文件,然后指示cmd.exe进行打印,我们可以在不使用C ++的情况下重现它:

“工作中”

非复制文件内容的屏幕截图 未复制问题的屏幕截图

“不工作”

复制文件内容的屏幕截图 重现问题的屏幕截图

(您可以使用“编辑” /“字符面板”菜单项在Notepad ++中插入特殊字符ASCII08。)

我的结论不是要依靠控制代码来实现这样的“技巧”:如果您想从字符串中删除字符,实际上是这样;如果要创建GUI,则可以这样做,或者使用像ncurses这样的聪明库来模拟一个GUI 。


zar*_*zar 3

控制台显示的是正确的输出,即

Group 4 output:
012
56789
Run Code Online (Sandbox Code Playgroud)

你错误地期待

Group 4 output:
01
56789
Run Code Online (Sandbox Code Playgroud)

字符的作用\b是将光标向后移动一个字符,但不会删除它。所以发生的情况是光标移回 2,但字符仍然在那里。

012
  ^
Run Code Online (Sandbox Code Playgroud)

下一个字符\n不是可打印字符,而是控制字符,它只是将光标移动到下一行,因此不会覆盖已打印的字符。

如果你这样做:

//This is group 4
numString[3] = '\b';
numString[4] = 'X';
cout << "Group 4 output:\n" << numString << endl;
Run Code Online (Sandbox Code Playgroud)

现在\b移至 2,但下一个字符“X”立即覆盖它,并按预期产生以下结果。

Group 4 output:
01X56789
Run Code Online (Sandbox Code Playgroud)

另一个演示是即使您添加另一个退格键:

numString[3] = '\b';
numString[4] = '\b';
numString[5] = '\n';
Run Code Online (Sandbox Code Playgroud)

光标现在位于 1

012
 ^
Run Code Online (Sandbox Code Playgroud)

现在它遇到\n(新行)作为下一个字符,它只是将光标移动到下一行,因此 1 和 2 永远不会被覆盖,因为它们已经被打印并保留在上一行

正如预期的那样,现在的输出是:

Group 4 output:
012
6789
Run Code Online (Sandbox Code Playgroud)

另请参阅这个那个