I have following code snippet to modify the stringstream such that it will have additional one char infront. But, i couldn't get the expected result.
void modifyStream(std::istream& s1)
{
if (s1.putback('Y'))
std::cout << s1.rdbuf() << '\n';
else
std::cout << "putback failed\n";
}
int main(int argc, const char * argv[])
{
const char* str = "Hello, world";
std::stringstream s1(str); // IO stream
modifyStream(s1);
return 0;
}
Output: putback failed
But, i am expecting the output of "YHello, world".
Can someone help to resolve this?
There is a misunderstanding about the putback method in your code: Its purpose is not to prepend data to the stream but to replace one already extracted character with another (you do not add anything, you replace an old character with something new). This is a bit more understandable by reading the documentation of sputbackc which is called by putback.
Example (from encppreference):
std::stringstream s1("Hello, world"); // IO stream
s1.get();
if (s1.putback('Y')) // modifies the buffer
std::cout << s1.rdbuf() << '\n';
else
std::cout << "putback failed\n";
Output:
Yello, world
The call s1.putback('Y') puts the character 'Y' back at the place of the last extracted character (in this case 'H').
Related
The question comes from https://en.cppreference.com/w/cpp/io/basic_istream/putback, the example code.
#include <sstream>
#include <iostream>
int main()
{
std::istringstream s2("Hello, world"); // input-only stream
s2.get();
if (s2.putback('Y')) // cannot modify input-only buffer
std::cout << s2.rdbuf() << '\n';
else
std::cout << "putback failed\n";
s2.clear();
if (s2.putback('H')) // non-modifying putback
std::cout << s2.rdbuf() << '\n';
else
std::cout << "putback failed\n";
}
Why s2.putback('Y') fails but s2.putback('H') succeed? Isn't the latter also an operation to modify the input-only stream buffer?
Also, I find something confusing while doing some experiments.
I add 1 line code compared to the sample above and the second results fails.. Why is it?
#include <sstream>
#include <iostream>
int main()
{
std::istringstream s2("Hello, world"); // input-only stream
s2.get();
if (s2.putback('Y')) // cannot modify input-only buffer
std::cout << s2.rdbuf() << '\n';
else
std::cout << "putback failed\n";
std::cout << s2.rdbuf() << '\n'; //1 line code added
s2.clear();
if (s2.putback('H')) // non-modifying putback
std::cout << s2.rdbuf() << '\n';
else
std::cout << "putback failed\n";
}
Why s2.putback('Y') fails but s2.putback('H') succeed? Isn't the latter also an operation to modify the input-only stream buffer?
The invocation s2.putback('H') potentially modifies the buffer, but in this case, it does not, because the data already start with an 'H'.
You can exemplify the behavior like this:
s2.clear();
assert(s2.putback('H')); // Ok, replacing 'H' with 'H' doesn't change anything
assert(!s2.putback('Z')); // Can't modify.
You can read further the behavior of sputbackc.
If a putback position is available in the get area (gptr() > eback()), and the character c is equal to the character one position to the left of gptr() (as determined by Traits::eq(c, gptr()[-1]), then simply decrements the next pointer (gptr()).
So in the case of s2.putback('H'), only the next pointer is decremented. The buffer is not changed.
Answer to your edit: basic_ostream& operator<<( std::basic_streambuf<CharT, Traits>* sb); extracts the characters maintained by sb, so after std::cout << s2.rdbuf() << '\n'; the next pointer points to the end of the buffer, which causes s2.putback('H') to fail.
I have an issue using stringstreams
When I run this code the first printf is fine but at some point it gets contaminated and prints out a shorter string.
string CJpsURI::getURIwithSipIpPort()
{
stringstream ss;
ss << "sip:" << m_ipAddr << ":" << m_portAddr;
string out = ss.str();
}
main() {
CJpsURI localIp();
localIp.setIpPort("192.168.88.1", 5060);
char *s = localIp.getURIwithSipIpPort().c_str();
printf("This is the sip port: %s", s); // this may be ok -- sip:192.168.88.1:5060
// do some stuff
printf("This is the sip port: %s", s); // wrong; -- sip:192.168.8/030/004
}
It almost appears that the *s is pointing to the out string on the stack which gets destroyed. But that should not be happening since I am returning out and not a reference to out.
But this seems to work.
string CJpsURI::getURIwithSipIpPort()
{
string out = (boost::format("sip:%1%:%2%") % m_ipAddr % m_portAddr).str();
return out;
}
main() {
CJpsURI localIp();
localIp.setIpPort("192.168.1.1", 5060);
char *s = localIp.getURIwithSipIpPort().c_str();
printf("This is the sip port: %s", s); // this may be ok
// do some stuff
printf("This is the sip port: %s", s); // this will be ok too;
}
Any ideas would be appreciated
You have two problems:
As remyabel and Galik pointed out: Your function does not return. So I would Concur with Galik that you need to adapt your function to:
string CJpsURI::getURIwithSipIpPort()
{
stringstream ss;
ss << "sip:" << m_ipAddr << ":" << m_portAddr;
return ss.str();
}
A char* points to an array of chars. Here you want the array found inside the string returned by getURIwithSipIpPort. But that memory will be released as soon as this line ends! There is nothing hanging on to it. So what you really need to do is:
string s{localIp.getURIwithSipIpPort()};
The C++ standard says:
§ 6.7/2 [..] Flowing off the end of a function is equivalent to a
return with no value; this results in undefined behavior in a
value-returning function.
The second problematic statement is:
char *s = localIp.getURIwithSipIpPort().c_str();
First, the conversion from const char* to char* is deprecated. Use const char*. Second, don't do this in the first place. If you want to prolong the lifetime of a temporary, use a const reference.
const std::string& s = localIp.getURIwithSipIpPort().c_str();
Your first function does not return anything:
string CJpsURI::getURIwithSipIpPort()
{
stringstream ss;
ss << "sip:" << m_ipAddr << ":" << m_portAddr;
string out = ss.str(); // This does not get returned!
}
Try this:
string CJpsURI::getURIwithSipIpPort()
{
stringstream ss;
ss << "sip:" << m_ipAddr << ":" << m_portAddr;
return ss.str(); // Now it returns something!
}
I have a ostringstream variable which contains some data.
I want to get set a char * pointer to the data inside the ostringstream.
If I do the following:
std::ostringstream ofs;
.....
const char *stam = (ofs.str()).c_str();
There is a copy of the content of the string in ofs.
I want to get a pointer to that content without a copy.
Is there a way to do so?
This actually answers the question... took a while but I wanted to do it for the same reasons (efficiency vs portability is fine for my situation):
class mybuf : public std::stringbuf {
public:
// expose the terribly named end/begin pointers
char *eback() {
return std::streambuf::eback();
}
char *pptr() {
return std::streambuf::pptr();
}
};
class myos : public std::ostringstream {
mybuf d_buf;
public:
myos() {
// replace buffer
std::basic_ostream<char>::rdbuf(&d_buf);
}
char *ptr();
};
char *myos::ptr() {
// assert contiguous
assert ( tellp() == (d_buf.pptr()-d_buf.eback()) );
return d_buf.eback();
}
int main() {
myos os;
os << "hello";
std::cout << "size: " << os.tellp() << std::endl;
std::string dat(os.ptr(),os.tellp());
std::cout << "data: " << dat << std::endl;
}
This points to, yet again, the deeper, underlying problem with the standard library - a confusion between contracts and "safety". When writing a messaging service, I need a library with efficient contracts... not safety. Other times, when writing a UI, I want strong safety - and cares less about efficiency.
Although you can't get a pointer to the character buffer in the ostringstream, you can get access to its characters without copying them if you switch to using stringstream. A stringstream allows input and output (reading from and writing to the stream), whereas ostringstream allows only output (writing to the stream). Example:
std::stringstream ss;
ss << "This is a test.";
// Read stringstream from index 0. Use different values to look at any character index.
ss.seekg(0);
char ch;
while (ss.get(ch)) { // loop getting single characters
std::cout << ch;
}
ss.clear(); // Clear eof bit in case you want to read more from ss
This site has a pretty good overview of stringstreams and what you can do with them.
Consider:
std::string s_a, s_b;
std::stringstream ss_1, ss_2;
// at this stage:
// ss_1 and ss_2 have been used and are now in some strange state
// s_a and s_b contain non-white space words
ss_1.str( std::string() );
ss_1.clear();
ss_1 << s_a;
ss_1 << s_b;
// ss_1.str().c_str() is now the concatenation of s_a and s_b,
// <strike>with</strike> without space between them
ss_2.str( s_a );
ss_2.clear();
// ss_2.str().c_str() is now s_a
ss_2 << s_b; // line ***
// ss_2.str().c_str() the value of s_a is over-written by s_b
//
// Replacing line *** above with "ss_2 << ss_2.str() << " " << s_b;"
// results in ss_2 having the same content as ss_1.
Questions:
What is the difference between stringstream.str( a_value ); and
stringstream << a_value; and, specifically, why does the first not
allow concatenation via << but the second does?
Why did ss_1 automatically get white-space between s_a and s_b, but
do we need to explicitly add white space in the line that could
replace line ***: ss_2 << ss_2.str() << " " << s_b;?
The problem you're experiencing is because std::stringstream is constructed by default with ios_base::openmode mode = ios_base::in|ios_base::out which is a non-appending mode.
You're interested in the output mode here (ie: ios_base::openmode mode = ios_base::out)
std::basic_stringbuf::str(const std::basic_string<CharT, Traits, Allocator>& s) operates in two different ways, depending on the openmode:
mode & ios_base::ate == false: (ie: non-appending output streams):
str will set pptr() == pbase(), so that subsequent output will overwrite the characters copied from s
mode & ios_base::ate == true: (ie: appending output streams):
str will set pptr() == pbase() + s.size(), so that subsequent output will be appended to the last character copied from s
(Note that this appending mode is new since c++11)
More details can be found here.
If you want the appending behaviour, create your stringstream with ios_base::ate:
std::stringstream ss(std::ios_base::out | std::ios_base::ate)
Simple example app here:
#include <iostream>
#include <sstream>
void non_appending()
{
std::stringstream ss;
std::string s = "hello world";
ss.str(s);
std::cout << ss.str() << std::endl;
ss << "how are you?";
std::cout << ss.str() << std::endl;
}
void appending()
{
std::stringstream ss(std::ios_base::out | std::ios_base::ate);
std::string s = "hello world";
ss.str(s);
std::cout << ss.str() << std::endl;
ss << "how are you?";
std::cout << ss.str() << std::endl;
}
int main()
{
non_appending();
appending();
exit(0);
}
This will output in the 2 different ways as explained above:
hello world
how are you?
hello world
hello worldhow are you?
Suggest you read stringstream reference: http://en.cppreference.com/w/cpp/io/basic_stringstream
std::stringstream::str() Replaces the contents of the underlying string
operator<< Inserts data into the stream.
in a function, that gets unsigned char && unsigned char length,
void pcap_callback(u_char *args, const struct pcap_pkthdr* pkthdr, const u_char* packet)
{
std::vector<unsigned char> vec(packet, packet+pkthdr->len); // optimized from foo.
std::stringstream scp;
for (int i=0;i<pkthdr->len;i++) {
scp<<vec[i];
}
std::string mystr = std::string(scp.rdbuf()->str());
std::cout << "WAS: " << packet << std::endl;
std::cout << "GOOD: " << scp.str() << std::endl;
std::cout << "BAD: " << scp.str().c_str() << std::endl;
std::cout << "TEST: " << mystr.size() << std::endl;
assert(mystr.size() == pkthdr->len);
}
Results:
WAS: prints nothing (guess there is a pointer to const.. case)
GOOD: prints data
BAD: prints nothing
TEST, assert: prints that mystr.size() is equal to passed unsigned char size.
I tried:
string.assign(scp.rdbuf());
memcpy(char, scp.str(), 10);
different methods of creating/allocating temporary chars, strings
No help.. it is wanted to get a std::cout'able std::string that contains data, (which was picked from foo, which was unsigned char, which was packet data).
Guessing either the original foo may not be null-terminated, or the problem is something like this - simple, but can't get in.. what are the things to look for here?
(this code is another attempt to use libpcap, just to print packets in C++ way, without using known C++ magic wrappers like libpcapp).
For a quick test, throw in a check for scp.str().size() == strlen(scp.str().c_str()) to see if there are embedded '\0' characters in the string, which is what I suspect is happening.
I think you're going about this the wrong way. It looks like you're dealing with binary data here, in which case you can't expect to meaningfully output it to the screen as text. What you really need is a hex dump.
const unsigned char* ucopy = packet;
std::ios_base::fmtflags old_flags = std::cout.flags();
std::cout.setf(std::ios::hex, std::ios::basefield);
for (const unsigned char* p = ucopy, *e = p + pkthdr->len; p != e; ++p) {
std::cout << std::setw(2) << std::setfill('0') << static_cast<unsigned>(*p) << " ";
}
std::cout.flags(old_flags);
This will output the data byte-by-byte, and let you examine the individual hex values of the binary data. A null byte will simply be output as 00.
Check std::cout.good() after the failed output attempt. My guess is that there's some failure on output (i.e. trying to write a nonprintable character to the console), which is setting failbit on cout.
Also check to ensure the string does not start with a NULL, which would cause empty output to be the expected behavior :)
(Side note, please use reinterpret_cast for unsigned char *ucopy = (unsigned char*)packet; if you're in C++ ;) )