rph
rph

Reputation: 911

Print a string variable with its special characters

When I want to special characters in a string I should use "\":

std::string text("item \t new_item\n")

But, if I print this string, obviously, it will print:

item      new_item

Is there a way set std::cout to print all special characters:

item \t new_item \n

Upvotes: 2

Views: 19367

Answers (3)

jamesdlin
jamesdlin

Reputation: 89965

There is no built-in way to do this. You will need to escape characters manually. For example, in C, it'd be something like:

for (const char* p = text; *p != '\0'; ++p)
{
    int c = (unsigned char) *p;

    switch (c)
    {
        case '\\':
            printf("\\\\");
            break;
        case '\n':
            printf("\\n");
            break;
        case '\r':
            printf("\\r");
            break;
        case '\t':
            printf("\\t");
            break;

        // TODO: Add other C character escapes here.  See:
        // <https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences>

        default:
            if (isprint(c))
            {
                putchar(c);
            }
            else
            {
                printf("\\x%X", c);
            }
            break;
    }
}

(Note that I wrote the above answer while the question was still tagged with C, and I used printf because I find it to be simpler (especially for demonstrative purposes) than going through C++'s iostream formatting mechanism.)

Upvotes: 5

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145259

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

Upvotes: 1

Shravan40
Shravan40

Reputation: 9888

\t and \n will print a tab(4 white spaces) and a new line respectively.

If you want to print \t and \n as output. You have to add an extra \ as prefix before \t and \n.

std::string text("item \\t new_item \\n")

Upvotes: 6

Related Questions