can I std::find a string in a stringstream? - c++

I have a stringstream that I'd like to iterate and determine if a substring exists in it.
I know that I could just convert to a string and do std::string::find(), but I was just hoping to avoid the conversion from stringstream to string if possible.
I understand the following won't work because the istream_iterator uses char as its type (not string)
stringstream ssBody;
string sFindThis;
...
auto itr = std::find (
istreambuf_iterator<char>(ssBody),
istreambuf_iterator<char>(),
sFindThis
);
But can I somehow search for a string in stringstream with std::find or similar without a conversion to string?

The C++ standard does not define any std::[io]?stringstream methods for searching its contents.
Neither can you use std::istreambuf_iterator together with std::search(), since std::istreambuf_iterator is an input iterator, but std::search() requires a forward iterator.
The only effective way to search a string stream is to convert it to a std::string, first.

using pubsetbuf it is possible to associate a buffer with basic_stringbuf member and then search the buffer, however behavior of this function is implementation defined. explanations and the example are from http://en.cppreference.com/w/cpp/io/basic_stringbuf/setbuf
#include <iostream>
#include <sstream>
int main()
{
std::ostringstream ss;
char c[1024] = {};
ss.rdbuf()->pubsetbuf(c, 1024);
ss << 3.14 << '\n';
std::cout << c << '\n';
}

Related

Partial copy of a stringstream

How can I copy three characters beginning with the second one of str1 to str2? This code doesn't work:
#include <iostream>
#include <sstream>
#include <iterator>
#include <algorithm>
int main()
{
std::stringstream str1("0123456789");
std::stringstream str2;
str1.seekg(1, str1.beg);
std::copy(std::istream_iterator<char>(str1), std::istream_iterator<char>(str1)+3, std::ostream_iterator<char>(str2));
std::cout << str2.str() << '\n';
}
The expected output is:
123
This is a dummy test, but I'd like to work with larger stringstreams and efficient methods.
There is another standard library algorithm called std::copy_n that will do what you want. It takes a begin iterator and the number of elements to copy. Your program works if you change the std::copy line to:
std::copy_n(std::istream_iterator<char>(str1), 3, std::ostream_iterator<char>(str2));
Demo on Compiler Explorer
An improvement might be to use std::next instead of the seekg() to advance the first iterator to the desired offset (Thanks to Armin Montigny). That would look like:
std::copy_n(std::next(std::istream_iterator<char>(str1)), 3, std::ostream_iterator<char>(str2));
Use substr() of std::string. To use the it for std::stringstream use str1.str()
Then assign it to str::string str2.
Step 1: Take the string our of the std::stringstream.
Step 2: Perform the operation on std::string.
Step 3: Assign the string to std::stringstream
Other way to get the part of stringstream:
char c[10] = {};
std::istringstream input("This is sample text."); // std::stringbuf makes its entire
// buffer available for unblocking read
input.readsome(c, 5); // reads 'This ' and stores in c[0] .. c[4]
input.readsome(c, 9); // reads 'is sample' and stores in c[0] .. c[8]

Cannot get vector size when constructed from istreambuf_iterator

I'm trying to read a binary file full of std::complex<float>. I tried the following code, as suggested in this SO answer:
#include <complex>
#include <iostream>
#include <iterator>
#include <fstream>
#include <string>
#include <vector>
void readRawFile(const std::string inputFile){
std::ifstream input(inputFile, std::ios::binary);
std::vector<std::complex<float>> auxBuffer(std::istreambuf_iterator<std::complex<float>>(input), std::istreambuf_iterator<std::complex<float>>());
std::cout << "Number of raw samples read: " << auxBuffer.size();
}
int main(){
readRawFile("myRawFile.raw");
return 0;
}
And I get the following compilation error:
In function 'void readRawFile(std::string)': 12:59: error: request for member 'size' in 'auxBuffer', which is of non-class type 'std::vector<std::complex<float> >(std::istreambuf_iterator<std::complex<float> >, std::istreambuf_iterator<std::complex<float> > (*)())'
I don't understand why can't I access the size method of a vector I just created without compilation errors. I suppose it has something to do with how the vector is being created, but it seems strange to me that it doesn't give an error there instead.
Any explanation for this?
The answer you copied shows how to read raw characters from a stream into a buffer. That's a correct use of istreambuf_iterator.
You are trying to extract complex numbers from a stream. That's a completely different operation, which involves reading the characters and then parsing them with operator<<. That's not what istreambuf_iterator is for. The type istreambuf_iterator<complex<float>> would try to extract characters of type complex<float> from a basic_streambuf<complex<float>> which is nonsense. That's not a character type, and you can't have a streambuf containing complex numbers as its raw characters.
istreambuf_iterator is for reading single characters from a streambuf, not for parsing characters and interpreting them as (complex) numbers or other types.
You need to use std::istream_iterator<X> to extract X values from an istream, so after you fix the most vexing parse, you need to use std::istream_iterator<std::complex<float>> as the iterator type.
You've hit C++'s Most Vexing Parse. You declaration was interpreted as a function declaration:
You can either:
void readRawFile(const std::string inputFile){
std::ifstream input(inputFile, std::ios::binary);
auto start = std::istream_iterator<std::complex<float>>(input);
auto stop = std::istream_iterator<std::complex<float>>();
std::vector<std::complex<float>> auxBuffer(start, stop);
std::cout << "Number of raw samples read: " << auxBuffer.size();
}
Or use Uniform Brace Initialization (C++11):
void readRawFile(const std::string inputFile){
std::ifstream input(inputFile, std::ios::binary);
std::vector<std::complex<float>> auxBuffer{std::istream_iterator<std::complex<float>>(input), std::istream_iterator<std::complex<float>>()};
std::cout << "Number of raw samples read: " << auxBuffer.size();
}
Live Here

Using string array as function argument

I meant to write program which will simply delete single letters from the input given by user, let's say we've got some text like: "monkey eat banana" and we supposed to delete the letter 'a' from the text above.
The final output supposed to look like this:
'monkey et bnn'
I've got the code which works pretty much flawlessly with single strings, but I have to use getline() function to obtain some longer texts, that is why I have to declare array of string, in order to pass it's size in the second argument of getline() function, like so:
string text[256];
getline(text, 256);
I would like to use getline() function without giving a size of an array, but I think it's impossible, therefore I need to stick with string array instead of a string.
The problem I've got is that I don't know how to correctly pass array of string, to use it as function's argument. Here's my code;
#include <iostream>
#include <string>
using namespace std;
void deleteLetter(string &text[], char c)
{
size_t positionL = text.find(c);
if(positionL == string::npos)
cout << "I'm sorry, there is no such letter in text" << endl;
else
text.erase(positionL, positionL);
cout << "After your character removed: " << text << endl;
}
int main()
{
string str1[256];
char a = 'a';
cin.getline(str1, 256);
deleteLetter(str1, a);
}
I know it's elementary stuff, but still I can't figure it out on my own.
Perhpahs I should reach out for your help.
It sounds to me like you don't need an array of strings. Just to read as many characters the user types, into a string. getline should deal fine with this.
int main()
{
std::string str1; // just a string here, not an array.
std::getline (std::cin,str1);
deleteLetter(str1, 'a');
}
Now you should change the signature of DeleteLetter to take a single string as argument.
void deleteLetter(std::string& text, char c);
How your are going to implement deleteLetter is another question. The way you have it, it will delete only the first occurence of 'a'.
To read a string from console input (cin), you can use the getline() function:
std::string line;
std::getline(std::cin, line);
To remove all the occurrences of a given letter from a string, you can use the so called erase-remove idiom, with a combination of the string::erase() method and the std::remove() algorithm.
(Note that this idiom is usually showed applied to std::vector, but don't forget that a std::string can also be viewed as a "container of characters" stored in sequence, similar to vector, so this idiom can be applied to string content as well.)
To pass a std::string to functions/methods, use the usual C++ rules, i.e.:
If the function is observing the string (without modifying it), pass using const reference: const std::string &
If the function does modify the content of the string, you can pass using non-const reference: std::string &
A simple compilable code follows:
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
//
// NOTE:
// Since the content of 'text' string is changed by the
// removeLetter() function, pass using non-const reference (&).
//
void removeLetter(string& text, char letter)
{
// Use the erase-remove idiom
text.erase(remove(text.begin(), text.end(), letter),
text.end());
}
int main()
{
string line;
getline(cin, line);
cout << "Read string: " << line << endl;
removeLetter(line, 'a');
cout << "After removing: " << line << endl;
}
This is what I got with MSVC:
C:\Temp\CppTests>cl /EHsc /W4 /nologo test.cpp
test.cpp
C:\Temp\CppTests>test.exe
monkey eats banana
Read string: monkey eats banana
After removing: monkey ets bnn
It's not very clear to me from your question if you also want to pass vectors of strings around (probably in other parts of your code)...
Anyway, if you want a vector of strings (i.e. you want to store some strings in a vector container) you can simply combine these STL class templates like this:
std::vector<std::string> strings;
To pass that to functions/methods, use the usual C++ rules, i.e.:
If the function is observing the array of strings (without modifying it), pass using const references (const &): vector<string> &
If the function does modify the content of the vector, you can pass using non-const references (&): vector<string> &

How do I use write with stringstream?

I have a vector<char> of data which I want to write into std::stringstream.
I tried:
my_ss.write(vector.data(), vector.size());
...but it seems to put nothing into my_ss which I declared as follows:
std::stringstream my_ss( std::stringstream::binary);
Why write is not working (app does not crash and compiles with 0 errors, 0 warnings)?
For the "how do I do it" you can use a std::ostream_iterator:
std::copy(vector.begin(), vector.end(), std::ostream_iterator<char>(my_ss));
Complete example:
#include <iterator>
#include <sstream>
#include <vector>
#include <iostream>
#include <algorithm>
int main() {
std::vector<char> vector(60, 'a');
std::ostringstream my_ss;
std::copy(vector.begin(), vector.end(), std::ostream_iterator<char>(my_ss));
std::cout << my_ss.str() << std::endl;
}
You could also just use that to construct a string directly, without going via a stringstream at all:
std::string str(vector.begin(), vector.end()); // skip all of the copy and stringstream
Though you haven't given any code, it sounds like you probably just wrote:
std::stringstream my_ss (std::stringstream::binary);
If you wish to write to a stringstream you need to combine the flag std::stringstream::out in the constructor. If I'm right, then you would see things working fine if you changed this to:
std::stringstream my_ss (std::stringstream::out | std::stringstream::binary);
(Obviously if you wish to read from that stringstream you need to add std::stringstream::in)
UPDATE Now that you've given your code...yup, this is your specific problem. Note #awoodland's point about the fact that you can just construct a string from a vector of chars instead (if that's the only thing you were planning on doing with this stream.)
The default parameter for the mode of stringbuf in stringstream is out|in.
explicit basic_stringstream(ios_base::openmode _Mode =
ios_base::in | ios_base::out)
: _Mybase(&_Stringbuffer),
_Stringbuffer(_Mode)
{ // construct empty character buffer
}
You need to add stringstream::out if you pass something explicitly like stringstream:binary
Or just use std::ostringstream

How to use basic_istream when you have a std::string

I have a function that uses basic_istream as an argument, and I have a std::string with the data I need to pass it. How do I do that?
You can put the string data into a stream:
std::string x;
std::stringstream ss(x); // put string into stream
function_taking_stream(ss);
A full working example showing how to parse a json string using boost property_tree:
#include <iostream>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
int main ()
{
std::string jsonString ("{ \"my key\": \"my value\" }");
ptree pt;
std::stringstream ss(jsonString);
read_json(ss, pt);
std::cout << pt.get<std::string>("my key") << std::endl;
return 0;
}
I have a function that uses basic_istream as an argument...
So I'm assuming you have a function that accepts a variable of a type specialized from the basic_istream template.
...and I have a std::string with the data I need to pass it.
So I'm assuming you want to feed this data into the istream argument.
How do I do that?
You don't. You can't feed output to an input stream!
I think the OP means he has a basic_istream<E> to read from and wants to read to a std::string, not to a std::basic_string<E>.
You should probably write to the basic_string first then convert that to a std::string. If you want to ensure the conversion is correct you probably want a proper UTF-8 to UTF-16 converter.
If you know you are dealing with only ASCII bytes you can do something like:
std::basic_string<E> strTemp;
is >> strTemp;
std::string str( strTemp.begin(), strTemp.end() );
If you have a basic_ostream<E> you can similarly first construct a basic_string<E> from your std::string and then write that to the stream.
You can "specialise" your template function so that if E happens to be char the conversion is quick. For that purpose you can input the two strings as non-const references and the specialised version could just "swap".