There is no way to define "new" special characters.
But you can make the stream interpret specific characters to have new meanings (that you can define). You can do this using locals.
Some things to note:
The characters in the string "xyza"
is just a simple way of encoding a string. Escaped characters are C++ way of allowing you to represent representing characters that are not visible but are well defined. Have a look at an ASCII table and you will see that all characters in the range 00 -> 31
(decimal) have special meanings (often referred to as control characters).
See Here: http://www.asciitable.com/
You can place any character into a string by using the escape sequence to specify its exact value; i.e. \x0A
used in a string puts the "New Line" character in the string.
The more commonly used "control characters" have shorthand versions (defined by the C++ language). '\n' => '\x0A'
but you can not add new special shorthand characters as this is just a convenience supply by the language (its like a tradition that most languages support).
But given a character can you give it a special meaning in an IO stream. YES. You need to define a facet for a locale then apply that locale to the stream.
Note: Now there is a problem with applying locals to std::cin
/std::out
. If the stream has already been used (in any way) applying a local may fail and the OS may do stuff with the stream before you reach main()
and thus applying a locale to std::cin
/std::cout
may fail (but you can do it to file and string streams easily).
So how do we do it.
Lets use "Vertical Tab" as the character we want to change the meaning of. I pick this as there is a shortcut for it \v
(so its shorter to type than \x0B
) and usually has no meaning for terminals.
Lets define the meaning as new line and indent 3 spaces.
#include <locale>
#include <algorithm>
#include <iostream>
#include <fstream>
class IndentFacet: public std::codecvt<char,char,std::mbstate_t>
{
public:
explicit IndentFacet(size_t ref = 0): std::codecvt<char,char,std::mbstate_t>(ref) {}
typedef std::codecvt_base::result result;
typedef std::codecvt<char,char,std::mbstate_t> parent;
typedef parent::intern_type intern_type;
typedef parent::extern_type extern_type;
typedef parent::state_type state_type;
protected:
virtual result do_out(state_type& tabNeeded,
const intern_type* rStart, const intern_type* rEnd, const intern_type*& rNewStart,
extern_type* wStart, extern_type* wEnd, extern_type*& wNewStart) const
{
result res = std::codecvt_base::ok;
for(;(rStart < rEnd) && (wStart < wEnd);++rStart,++wStart)
{
if (*rStart == '\v')
{
if (wEnd - wStart < 4)
{
// We do not have enough space to convert the '\v`
// So stop converting and a subsequent call should do it.
res = std::codecvt_base::partial;
break;
}
// if we find the special character add a new line and three spaces
wStart[0] = '\n';
wStart[1] = ' ';
wStart[2] = ' ';
wStart[3] = ' ';
// Note we do +1 in the for() loop
wStart += 3;
}
else
{
// Otherwise just copy the character.
*wStart = *rStart;
}
}
// Update the read and write points.
rNewStart = rStart;
wNewStart = wStart;
// return the appropriate result.
return res;
}
// Override so the do_out() virtual function is called.
virtual bool do_always_noconv() const throw()
{
return false; // Sometime we add extra tabs
}
};
Some code that uses the locale.
int main()
{
std::ios::sync_with_stdio(false);
/* Imbue std::cout before it is used */
std::cout.imbue(std::locale(std::locale::classic(), new IndentFacet()));
// Notice the use of '\v' after the first lien
std::cout << "Line 1\vLine 2\nLine 3\n";
/* You must imbue a file stream before it is opened. */
std::ofstream data;
data.imbue(std::locale(std::locale::classic(), new IndentFacet()));
data.open("PLOP");
// Notice the use of '\v' after the first lien
data << "Loki\vUses Locale\nTo do something silly\n";
}
The output:
> ./a.out
Line 1
Line 2
Line 3
> cat PLOP
Loki
Uses Locale
To do something silly
BUT
Now writing all this is not really worth it. If you want a fixed indent like that us a named variable that has those specific characters in it. It makes your code slightly more verbose but does the trick.
#include <string>
#include <iostream>
std::string const newLineWithIndent = "\n ";
int main()
{
std::cout << " hello" << newLineWithIndent << "400";
}