Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing behaviour of double quotes when >> a stringstream

Here is what I'm trying to do:

say I have a stringstream. Then I << "\"hello world\" today";

then when I do

sstr >> myString1 >> myString2;

I would like myString1 to have "hello world" and for myString2 to have "today"

Is there a way, possibly with a manipulator, to achieve this?

Thanks

like image 758
jmasterx Avatar asked Jan 15 '11 19:01

jmasterx


4 Answers

Short Answer: No

Long Answer:
There are no manipulates that will do that for you.

Alternative Answer:

You could write your own type that could be used in conjunction with the stream operators do do this task.

#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>


class QuotedWord
{
    public:
        operator std::string const& () const { return data;}

    private:
        std::string     data;
      friend std::ostream& operator<<(std::ostream& str, QuotedWord const& value);
      {
        return str << value.data;
      }
      friend std::istream& operator>>(std::istream& str, QuotedWord& value);
      {
        char x;
        str >> x;
        if ((str) && (x == '"'))
        {
            std::string extra;
            std::getline(str, extra, '"');
            value.data = std::string("\"").append(extra).append("\"");
        }
        else
        {
            str.putback(x);
            str >> value.data;
        }
        return str;
      }
};

Then it can be used normally.

int main()
{
    QuotedWord  word;

    std::cin >> word;
    std::cout << word << "\n";

    // Easily convertible to string
    std::string tmp = word;
    std::cout << tmp << "\n"

    // because it converts to a string easily it can be used where string is needed.
    std::vector<std::string>   data;

    std::copy(std::istream_iterator<QuotedWord>(std::cin),
              std::istream_iterator<QuotedWord>(),

              // Notice we are using a vector of string here.
              std::back_inserter(data)
             );
}

> ./a.out
"This is" a test    // Input
"This is"           // Output
"This is"
like image 76
Martin York Avatar answered Oct 19 '22 04:10

Martin York


No.

You either need to change the stream type (and hence the parsing semantics) or use your own string type (and hence change the parsing semantics in your overloaded op>>).

Instead, consider writing a function, similar to getline, which parses a possibly-quoted "word" out of a stream:

getqword(sstr, myString1);
getqword(sstr, myString2);
if (sstr) { /* both succeeded */ }

Note that input into a std::string already terminates at whitespace, so you only need to handle peeking ahead for a quote and then handle edge cases, of which there are plenty:

  • escapes (quotes inside quotes)
    • the three prevalent styles are disallowed, backslash, and doubled quote; each has advantages and disadvantages
  • spanning multiple lines (can \n be included?)
    • this is often a data error, so disallowing it can be a big help in some circumstances
  • what happens if a quoted word ends next to another word?
    • "\"hello world\"today"
  • skip leading whitespace?
    • unlike getline, it makes sense to honor the stream's skipws flag, but I could see that going the other way in rare circumstances
    • the istream sentry will handle this for you
  • don't forget to use the stream's traits and locale, or use methods which already do that!
    • or document your assumptions
like image 25
Fred Nurk Avatar answered Oct 19 '22 05:10

Fred Nurk


Not directly, you need a "wrapper" class that terminates where you want it to.

struct QuotedStringReader
{
   std::string& str;
   QuotedStringReader( std::string& s ) : str( s ) {}
};

std::istream operator>>( std::istream&, const QuotedStringReader& qsr );

std::string s, s2;
stream >> QuotedStringReader( s ) << s2;

Note that this is a rare occasion where you stream into a const - because you can write to the internal str even though it is const, and this way I can pass in a temporary.

Actually as you probably don't know what you are about to read you could simply call it "TokenReader" which reads whatever you define as a "token".

like image 2
CashCow Avatar answered Oct 19 '22 03:10

CashCow


Nope. Not that particular way. You could create a "strong typedef" for string though and design a new operator>> for it that behaves that way.

like image 1
Edward Strange Avatar answered Oct 19 '22 03:10

Edward Strange