Simple Mistake
Simple Mistake

Reputation: 43

How to output two versions of a string, one with escape character and the other not, in C++?

I have one chance to define the string, lets say

string s = "abc\"def\\hhh\"i";

After this definition, I want to output (using ofstream to write to a text file) two versions of this string afterwards. The first one is the output of s by default:

abc"def\hhh"i

The second one I want is:

abc\"def\\hhh\"i

I am writing a sort of "recursive" definition, defining another string with extra escape characters is not a solution. I also looked up raw string, but it can only output the second not the first, and it is a feature for c++11, which is too new for some computers to compile.

How can I output the second version of the string without using c++11? If I have to use c++11, how to avoid defining the string twice?

Upvotes: 4

Views: 135

Answers (2)

zett42
zett42

Reputation: 27756

I tend to write something like this as template with range input and iterator output. This provides much flexibility as you can output to a stream, another string or anything else that you could wrap into an output iterator, all using the same function.

Input doesn't even have to be a std::string, it could be a std::vector, a simple array or any type for which an overload of begin() and end() is provided (requirements of range-for loop).

Another advantage compared to simply returning an std::string from the function is that you don't have to create a temporary string for the result which avoids memory allocations which should improve performance.

#include <iostream>
#include <string>
#include <iterator>

template< typename Range, typename OutputIterator >
OutputIterator copy_escaped( const Range& in, OutputIterator out ){
    for( const auto& c : in ){
        switch( c ){
            case '"': 
                *out++ = '\\';
                *out++ = '"';
                break;
            case '\\':
                *out++ = '\\';
                *out++ = '\\';
                break;
            case '\n':
                *out++ = '\\';
                *out++ = 'n';
                break;
            case '\r':
                *out++ = '\\';
                *out++ = 'r';
                break;
            case '\t':
                *out++ = '\\';
                *out++ = 't';
                break;
            // Could add more stuff to escape here
            // case ...:
            default:
                *out++ = c;
        }
    }
    return out;
}

You could easily extend the function to escape additional characters.

Usage examples:

int main()
{
    std::string s = "abc\"def\\hhh\"i";

    // output normal
    std::cout << s << std::endl;

    // output escaped
    copy_escaped( s, std::ostream_iterator<char>( std::cout ) );
    std::cout << std::endl;

    // output escaped to other string
    std::string escaped_s;
    escaped_s.reserve( s.size() );  // not required but improves performance
    copy_escaped( s, back_inserter( escaped_s ) );
    std::cout << escaped_s << std::endl;
}

Live demo.

Upvotes: 2

Peter Lenkefi
Peter Lenkefi

Reputation: 1346

It is very simple to write such functionality:

std::string to_quoted(std::string const& src) {
    std::string result;
    result.reserve(src.size());  // Not necessary but makes the code more efficient (mentioned in comments)
    for (std::size_t i = 0; i < src.length(); i++) {
        switch (src[i]) {
        case '"': result += "\\""; break;
        case '\\': result += "\\\\"; break;
        // ...
        default: result += src[i];
        }
    }
    return result;
}

There may be better solutions, I think this is the simplest and quickest one. I don't understand what you mean by "defining another string", maybe you mean constructing another string is disallowed, in that case just output to the stream instead of concatenating characters.

Upvotes: 4

Related Questions