开发者

Changing behaviour of double quotes when >> a stringstream

开发者 https://www.devze.com 2023-02-05 01:59 出处:网络
Here is what I\'m trying to do: say I have a stringstream. Then I << \"\\\"hello world\\\" today\";

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


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"


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


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".


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.


In c++14

#include <iostream>
#include <iomanip>
#include <sstream>

int main()
{
    std::stringstream ss;
    std::string in = "String with spaces, and embedded \"quotes\" too";
    std::string out;

    ss << std::quoted(in);
    std::cout << "read in     [" << in << "]\n"
              << "stored as   [" << ss.str() << "]\n";

    ss >> std::quoted(out);
    std::cout << "written out [" << out << "]\n";
}

Output:

read in     [String with spaces, and embedded "quotes" too]
stored as   ["String with spaces, and embedded \"quotes\" too"]
written out [String with spaces, and embedded "quotes" too]

*Source:https://en.cppreference.com/w/cpp/io/manip/quoted


That is not possible. It's because if you look into the implementation of operator>> for basic_istream which is in fact called when you do sstr >> myString1, you will see this inside the for loop:

else if (_Ctype_fac.is(_Ctype::space,_Traits::to_char_type(_Meta)))
   break;// whitespace, quit

Means, once you get "space", Quit. So you cannot keep adding characters to your myString1 once you get space.

Note this is MSVC++ implementation, but I'm sure the equivalent you'll found in all implementation!


You can overload the input stream operator and include the parsing semantics there.

std::istream& operator>>(std::istream& is, std::string& out)
{
    char c;
    is >> c;
    if (c == '\"')
    {
        std::getline(is, out, '\"');
        return is;
    }
    else
    {
        is.putback(c);
        return std::operator >>(is, out);
    }
}

int main()
{
    std::istringstream iss("\"hello world\" today");
    std::string test;
    while (iss >> test)
        std::cout << test << std::endl;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消