I'm using c++ string with special characters for console output. Most of the results can be predicted, but one of them fell out of my expectation. I could not find answers anywhere.
Platform: Windows 7 Enterprise Version 6.1 (Build 7601: Service Pack 1) Compiler: 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;
}
The output in console:
Group 1 output: 01256789 Group 2 output: 01 56789 Group 3 output: 012 56789 Group 4 output: 012 56789 Group 5 output: 01 56789
The 4th group output is expected as,
Group 4 output: 01 56789
while the output actually is,
Group 4 output: 012 56789
What I can't understand is why the character '2' is still there.
Anyone could please help me understand the problem? Thank you.
After seeing the answers below, especially zar's, I believe I have understood the problem, and would like to summarize a bit here.
Please pay attention to move the cursor.
I'd like to paste all of the code here:
//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;
}
And the output below for better understanding these special characters:
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
The backslash character ( \ ) is a line-continuation character when it's placed at the end of a line. If you want a backslash character to appear as a character literal, you must type two backslashes in a row ( \\ ). For more information about the line continuation character, see Phases of Translation.
C++ assigns special meaning to the backslash within a string literal and requires it to be escaped to be read as an actual backslash: To represent a single backslash, it's necessary to place double backslashes (\\) in the source code. (Exception: Raw literals, supported by C++11, remove the need to escape characters.)
This comes down to your terminal. We can see easily from a platform that doesn't render the control character '\b'
in any special way that it's present in the string at the expected location:
So, why doesn't it "erase" the 2
?
If we open up cmd.exe and type in A, B, Ctrl+H then we see the B
is immediately erased. This would seem to disprove the notion that cmd.exe handles backspace "non-destructively" as many consoles do.
But it doesn't disprove it! This seems to be a special handling for keystrokes, presumably tied into how the actual backspace character works. After all, you want the backspace character to actually erase things, rather than just moving the cursor.
cmd.exe treats the control character differently when found in output not generated by the keyboard: in a non-destructive fashion. So it moves the cursor backwards then the next character "overwrites" the would-be erased character.
But in group 4, you have a newline, so the next character goes on the next line and is not in the right place to erase anything.
We can reproduce this without C++, by constructing a special file then instructing cmd.exe to print it:
"Working"
"Not working"
(You can insert the special character ASCII 08 in Notepad++, using the "Edit"/"Character Panel" menu item.)
My conclusion is not to rely on control codes for such "tricks": if you want to remove a character from a string, actually do so; if you want to create a GUI, either actually do so, or simulate one with a clever library like ncurses.
What the console is showing is the correct output, that is
Group 4 output:
012
56789
You are mistaken to expect
Group 4 output:
01
56789
What \b
character does is move the cursor one character back, it does not delete it. So what has happened is the cursor is moved back to 2 but the character is still there.
012
^
The next character \n
is not a printable character but a control character and it just moves the cursor to the next line so it doesn't overwrite that character which was already printed.
If you do this instead:
//This is group 4
numString[3] = '\b';
numString[4] = 'X';
cout << "Group 4 output:\n" << numString << endl;
Now \b
moves to 2 but the next character 'X' immediately overwrites it yielding the following result as expected.
Group 4 output:
01X56789
Another demonstration is even if you add another backspace:
numString[3] = '\b';
numString[4] = '\b';
numString[5] = '\n';
The cursor is now at 1
012
^
Now it encounters the \n
(new line) as next character and it simply moves the cursor to the next line so 1 and 2 are never overwritten as they were already printed and remains now in the previous line.
The output hence now is, as expected:
Group 4 output:
012
6789
See also this and that
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With