【问题标题】:Can't understand the output of C++ string with special characters无法理解带有特殊字符的 C++ 字符串的输出
【发布时间】:2019-06-12 13:12:50
【问题描述】:

我正在使用带有特殊字符的 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;
}

控制台输出:

第 1 组输出: 01256789 第 2 组输出: 01 56789 第 3 组输出: 012 56789 第 4 组输出: 012 56789 第 5 组输出: 01 56789

第 4 组输出预期为,

第 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;
}

为了更好地理解这些特殊字符,下面的输出:

第 1 组输出: 01256789 第 2 组输出: 01 56789 第 3 组输出: 012 56789 第 4 组输出: 012 56789 第 5 组输出: 01 56789 第 6 组输出: 03 56789 第 7 组输出: 0156789 第 8 组输出: 56789 第 9 组输出: 012 967

【问题讨论】:

  • 第四组没有变化numString[2]。这只发生在第 5 组
  • 无法重现输出:godbolt。 (第二行)。
  • 不确定\n 的输出是否会覆盖显示的字符。
  • 另请注意,在大多数控制台上,\b 是非破坏性退格键。
  • 关于您的“组”的实际输出不是很清楚。也许字符串末尾的第二个换行符会使它更清晰?或者打印一个标题,比如std::cout &lt;&lt; "\nGroup X\n" &lt;&lt; numString;(你用“组”号替换X)。

标签: c++ string special-characters


【解决方案1】:

这取决于您的终端。我们可以从一个不以任何特殊方式呈现控制字符 '\b' 的平台轻松地看到 it's present in the string at the expected location

那么,它为什么不“删除”2

如果我们打开 cmd.exe 并输入 A, B, Ctrl+H 然后我们看到B 立即被删除。这似乎反驳了 cmd.exe 处理退格“非破坏性”as many consoles do 的观点。

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

cmd.exe 在不是由键盘生成的输出中发现控制字符时会以不同的方式处理控制字符:以非破坏性方式。所以它向后移动光标,然后下一个字符“覆盖”将被删除的字符。

但是在第 4 组中,你有一个换行符,所以下一个字符在下一行,并且不在正确的位置删除任何内容。

我们可以在不使用 C++ 的情况下重现这一点,方法是构建一个特殊文件,然后指示 cmd.exe 打印它:

“工作”

“不工作”

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

我的结论是不要依赖控制代码来实现这种“技巧”:如果你想从字符串中删除一个字符,那么就这样做吧;如果你想创建一个 GUI,要么真的去做,要么用一个聪明的库来模拟一个,比如 ncurses

【讨论】:

  • 感谢您如此详细地回答我的问题!
  • @ShaunYu 没问题,这很有趣:D
【解决方案2】:

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

Group 4 output:
012
56789

你误会了

Group 4 output:
01
56789

\b 字符所做的是将光标向后移动一个字符,它删除它。所以发生的事情是光标移回 2 但字符仍然存在。

012
  ^

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

如果您改为这样做:

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

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

Group 4 output:
01X56789

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

numString[3] = '\b';
numString[4] = '\b';
numString[5] = '\n';

光标现在位于 1

012
 ^

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

因此现在的输出正如预期的那样:

Group 4 output:
012
6789

另请参阅thisthat

【讨论】:

  • 感谢 zar 说出这些控制字符的真相并帮助我彻底理解问题。
猜你喜欢
  • 2019-07-12
  • 2020-07-12
  • 1970-01-01
  • 2013-04-15
  • 2012-03-23
  • 1970-01-01
  • 1970-01-01
  • 2011-04-11
  • 1970-01-01
相关资源
最近更新 更多