无法理解带有特殊字符的 C++ 字符串的输出
Can't understand the output of C++ string with special characters
我正在使用带有特殊字符的 C++ 字符串作为控制台输出。大部分结果都是可以预料的,但有一个结果出乎我的意料。我到处都找不到答案。
平台:
Windows 7 企业版 6.1(内部版本 7601:Service Pack 1)
编译器:
g++ (海湾合作委员会) 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;
}
控制台输出:
Group 1 output:
01256789
Group 2 output:
01 56789
Group 3 output:
012
56789
Group 4 output:
012
56789
Group 5 output:
01
56789
第 4 组输出预期为,
Group 4 output:
01
56789
而实际输出是,
Group 4 output:
012
56789
我不明白为什么字符'2'仍然存在。
谁能帮我理解这个问题?
谢谢。
看到下面的答案,尤其是zar的,我相信我已经明白了问题所在,想在这里总结一下。
- Windows 当未检测到物理击键时,cmd 控制台处于非破坏性模式。
- 任何新的输出都会开始覆盖当前光标中的现有输出。听起来多余但很有必要。如果有任何新的字符输出,它将覆盖现有的,直到新字符用完。如果还有更多现有字符,它们将继续存在于那里并且可能看起来像 "output" 在新字符后面。
- '\b' 仅将光标 向后移动一个字符。它不会删除任何内容。
- '\n'仅将光标移动到下一行。它不会将其后面的任何字符移动到下一行。
- '\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;
}
下面的输出可以更好地理解这些特殊字符:
Group 1 output:
01256789
Group 2 output:
01 56789
Group 3 output:
012
56789
Group 4 output:
012
56789
Group 5 output:
01
56789
Group 6 output:
03
56789
Group 7 output:
0156789
Group 8 output:
56789
Group 9 output:
012
967
这取决于您的终端。我们可以很容易地从一个不以任何特殊方式呈现控制字符 '\b'
的平台上看到 it's present in the string at the expected location:
那么,为什么 "erase" 2
?
如果我们打开cmd.exe并输入A, B, Ctrl+H 然后我们看到 B
立即被擦除。这似乎反驳了 cmd.exe 处理退格 "non-destructively" as many consoles do.
的观点
但不反驳!这似乎是对击键的特殊处理,大概与实际退格字符的工作方式有关。毕竟,您希望退格键真正擦除内容,而不是仅仅移动光标。
cmd.exe 在不是由键盘生成的输出中发现控制字符时,会以不同方式处理控制字符:以 non-destructive 方式。所以它将光标向后移动然后下一个字符 "overwrites" would-be 被删除的字符。
但是在第 4 组中,你有一个换行符,所以下一个字符在下一行并且不在正确的位置以擦除任何内容。
我们可以在没有 C++ 的情况下重现它,方法是构建一个特殊文件然后指示 cmd.exe 打印它:
"Working"
"Not working"
(您可以在 Notepad++ 中插入特殊字符 ASCII 08,使用 "Edit"/"Character Panel" 菜单项。)
我的结论是不依赖控制代码"tricks":如果你想从字符串中删除一个字符,实际这样做;如果你想创建一个 GUI,要么实际这样做,要么用像 ncurses.
这样的聪明的库模拟一个
控制台显示的是正确的输出,即
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 永远不会被覆盖,因为它们已经打印出来并且现在保留在 前行。
现在的输出如预期的那样:
Group 4 output:
012
6789
另见 this and that
我正在使用带有特殊字符的 C++ 字符串作为控制台输出。大部分结果都是可以预料的,但有一个结果出乎我的意料。我到处都找不到答案。
平台: Windows 7 企业版 6.1(内部版本 7601:Service Pack 1) 编译器: g++ (海湾合作委员会) 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;
}
控制台输出:
Group 1 output: 01256789 Group 2 output: 01 56789 Group 3 output: 012 56789 Group 4 output: 012 56789 Group 5 output: 01 56789
第 4 组输出预期为,
Group 4 output: 01 56789
而实际输出是,
Group 4 output: 012 56789
我不明白为什么字符'2'仍然存在。
谁能帮我理解这个问题? 谢谢。
看到下面的答案,尤其是zar的,我相信我已经明白了问题所在,想在这里总结一下。
- Windows 当未检测到物理击键时,cmd 控制台处于非破坏性模式。
- 任何新的输出都会开始覆盖当前光标中的现有输出。听起来多余但很有必要。如果有任何新的字符输出,它将覆盖现有的,直到新字符用完。如果还有更多现有字符,它们将继续存在于那里并且可能看起来像 "output" 在新字符后面。
- '\b' 仅将光标 向后移动一个字符。它不会删除任何内容。
- '\n'仅将光标移动到下一行。它不会将其后面的任何字符移动到下一行。
- '\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;
}
下面的输出可以更好地理解这些特殊字符:
Group 1 output: 01256789 Group 2 output: 01 56789 Group 3 output: 012 56789 Group 4 output: 012 56789 Group 5 output: 01 56789 Group 6 output: 03 56789 Group 7 output: 0156789 Group 8 output: 56789 Group 9 output: 012 967
这取决于您的终端。我们可以很容易地从一个不以任何特殊方式呈现控制字符 '\b'
的平台上看到 it's present in the string at the expected location:
那么,为什么 "erase" 2
?
如果我们打开cmd.exe并输入A, B, Ctrl+H 然后我们看到 B
立即被擦除。这似乎反驳了 cmd.exe 处理退格 "non-destructively" as many consoles do.
但不反驳!这似乎是对击键的特殊处理,大概与实际退格字符的工作方式有关。毕竟,您希望退格键真正擦除内容,而不是仅仅移动光标。
cmd.exe 在不是由键盘生成的输出中发现控制字符时,会以不同方式处理控制字符:以 non-destructive 方式。所以它将光标向后移动然后下一个字符 "overwrites" would-be 被删除的字符。
但是在第 4 组中,你有一个换行符,所以下一个字符在下一行并且不在正确的位置以擦除任何内容。
我们可以在没有 C++ 的情况下重现它,方法是构建一个特殊文件然后指示 cmd.exe 打印它:
"Working"
"Not working"
(您可以在 Notepad++ 中插入特殊字符 ASCII 08,使用 "Edit"/"Character Panel" 菜单项。)
我的结论是不依赖控制代码"tricks":如果你想从字符串中删除一个字符,实际这样做;如果你想创建一个 GUI,要么实际这样做,要么用像 ncurses.
这样的聪明的库模拟一个控制台显示的是正确的输出,即
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 永远不会被覆盖,因为它们已经打印出来并且现在保留在 前行。
现在的输出如预期的那样:
Group 4 output:
012
6789
另见 this and that