The C++ standard library has no direct support for escaping strings.
You can implement it fairly directly, but this runs into three problems:
There are non-standard escapes such as \e
for ASCII's escape character (code 27), supported by e.g. g++.
The number of bits per char
(CHAR_BIT
from <limits.h>
), and hence the necessary number of digits in a numerical escape, is system-dependent.
Whether a character is printable or not, i.e. whether it needs escaping, depends on the locale. And this gets further complicated by the number of competing indicators of current locale: the C level global locale, the C++ level global locale, or e.g. the locale imbued in cout
?
With some hardwired choices about that, implementation code can look like this:
#include <stdio.h> // ::sprintf
#include <ctype.h> // ::isprint
#include <string> // std::string
#include <unordered_map> // std::unordered_map
namespace my{
using std::string;
using std::unordered_map;
auto string_from( char const ch )
-> string
{ return string( 1, ch ); } // Note: "string{1,ch}" is something else.
auto is_printable( char const ch )
-> bool
{ return !!::isprint( static_cast<unsigned char>( ch ) ); }
auto escaped( char const ch )
-> string
{
static unordered_map<char, string> const escapes =
{
{ '\a', "\\a" }, // 7, ^G, alert (bell)
{ '\b', "\\b" }, // 8, ^H, backspace
{ '\t', "\\t" }, // 9, ^I, tab
{ '\n', "\\n" }, // 10, ^J, newline / linefeed
{ '\v', "\\v" }, // 11, ^K, vertical tab
{ '\f', "\\f" }, // 12, ^L, formfeed
{ '\r', "\\r" }, // 13, ^M, carriage return
{ 27, "\\e" }, // 27, ^[, escape (NON-STANDARD)
{ '\\', "\\\\" } // backslash
};
auto const it = escapes.find( ch );
if( it != escapes.end() )
{
return it->second;
}
else if( is_printable( ch ) )
{
return string_from( ch );
}
else
{
int const code = static_cast<unsigned char>( ch );
char buf[] = "\\xDDDD";
sprintf( buf + 2, "%04X", code );
return buf;
}
}
auto escaped( string const& s )
-> string
{
string result;
for( char const ch: s )
{
result += escaped( ch );
}
return result;
}
} // namespace my
#include <iostream>
#include <locale.h>
using namespace std;
auto main()
-> int
{
using my::escaped;
auto const text = "item \t \xC7\x81new_item\n"s;
setlocale( LC_ALL, "" );
cout << escaped( text ) << '\n';
}