If I have a std::stringstream variable and I wrote some std::string objects in it, and I want to be able to track the stream position pointers and output them on the screen, how do I use the tellp()
and then later on when I extract from the stream i.e. using the stream for input, how do I use tellg().
How do I open the stream in input mode again? Also, should I set the position pointer back to 0 with seekg() or seekp() when I open it back again for input?
Here is my code
std::string word;
std::stringstream ss;
while (word != "n") {
std::cout << "Enter a word, ( n to stop) : ";
std::cin >> word;
std::cin.ignore();
if (word == "n")
ss << std::endl;
else {
ss << word;
ss << " ";
}
}
std::cout << "The content in stringstream is : " << ss.str() << std::endl;
std::cout << "The StreamPositionPointer is"
<< ss.tellp(); // is this correct way of outputting streampointer
// how should I open the stream to get input from it to store it in a variable?
/* how should I switch the position pointer to a position when I use an
input/output stream like sstream with seekg(), seekp(), tellg(),tellp() */
You have the basic idea. The std::basic_stringstream allows use of both std::basic_istream::tellg and std::basic_istream::seekg. There are a few caveats:
if using the .str() member funciton, a copy of the underlying string is returned in a temporary object, there is no change to the seekpos within the stringstream because you are operating on a copy;
if using the rdbuf() member function, it will return a pointer to the underlying string device. Outputting with the device will output the contents with and the seekpos will be advanced to the end, but eofbit is not set;
for normal extractions with >> reading the last item in the stringstream sets eofbit and clear() must be called on the stringstream before seeking further. If tellg() is called while the stringstream the streamstate is anything other than good, -1 is returned.
With that a short example exercising seekg() and tellg() along with clear(), where needed, should provide the explanation you are looking for. The is an amalgam of the various examples provided at the links above:
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
int main (void)
{
std::string str = "Hello, world"; /* initial string */
std::istringstream in(str); /* initialized stringstream */
std::string word1, word2; /* two string variables used below */
std::cout << "string: " << in.str() << "\n\ntellg "
<< std::setw(2) << in.tellg() << " - reading into word1\n";
in >> word1; /* read into word1 from beginning */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - after reading \""
<< word1 << "\", rewinding\n";
in.seekg (0); /* seekg beginning (rewind) */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - reading into word2\n";
in >> word2; /* read into word2 from beginning */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - after reading \""
<< word2 << "\", reading final word\n";
in >> word2; /* read into word2 to end of stringstream, eofbit set */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - after reading \""
<< word2 << "\", eofbit set, tellg() fails, must .clear()\n";
in.clear(); /* clear required before further stringstring operations */
in.seekg (0, std::ios_base::beg); /* reposition to beginning, 2nd form */
std::cout << "tellg " << std::setw(2) << in.tellg() << " - rdbuf() -> \""
<< in.rdbuf()
<< "\"\ntellg " << std::setw(2) << in.tellg() << " - reversing: \"";
while (in.tellg() != 0) { /* playing tell & seek back to beginning */
in.seekg (-1, std::ios_base::cur); /* seek -1 from current */
std::cout << (char)in.get(); /* output character */
in.seekg (-1, std::ios_base::cur); /* seek -1 from current */
}
std::cout << "\"\ntellg " << std::setw(2) << in.tellg() << " - all done.\n";
}
Eample Use/Output
$ ./bin/streambuf_seektellg
string: Hello, world
tellg 0 - reading into word1
tellg 6 - after reading "Hello,", rewinding
tellg 0 - reading into word2
tellg 6 - after reading "Hello,", reading final word
tellg -1 - after reading "world", eofbit set, tellg() fails, must .clear()
tellg 0 - rdbuf() -> "Hello, world"
tellg 12 - reversing "dlrow ,olleH"
tellg 0 - all done.
The example above shows how to handle manipulation of the streambuf position within the stringstring. From simple rewinds, to handling the streambuf with the read-state other than goodbit and how to use combinations of tellg() and seekg() to iterate over each of the characters in the streambuf from the end to the beginning effectively reversing the output.
Additional Example Using seekp() On std::stringstream
In response to your comment, there really isn't much difference, except in order to use the tellp() and seekp() the streambuf object must be able to have output sent to it. That means tellp() and seekp() are only relevant to std::stringstream and std::ostringstream but not std::istringstream. So in order to position the write location in the example above, you need only change the type to one of two variants that allows output. using std::stringstream is fine, e.g.
std::stringstream in(str); /* stringstrem use both seek&tell (gp) */
Now you can move the get and set the write position within it using tellp() and seekp(). For example the following gets the current read position in in by reading again into word1 and saves the position reported by in.tellg(). The position is then used by seekp() to position the for a write of "big wide world!" after "Hello, ", e.g.
std::cout << "\"\ntellg " << std::setw(2) << in.tellg()
<< " - seeking with seekp() - add \"big wide world!\"\n";
in >> word1; /* move past "Hello," */
size_t pos = in.tellg(); /* save offset from beginning */
in.seekp (pos + 1); /* seekp() past "Hello, " */
in << "big wide world!"; /* replace remaining content */
std::cout << "\nstring: " << in.str() << '\n'; /* output result */
Changes to Output
Changes to the last line of output above and then addition of the new text would result in:
...
tellg 0 seeking with seekp() - add "big wide world!"
string: Hello, big wide world!
seekg()/tellg() and seekp()/tellp() are Independent Offsets
In response to your further comments, it is important to understand that the ...g() and ...p() member functions provide access to two independent measures of offset from the beginning of the streambuf object. seekg() and tellg() are associated with the read position in the buffer. (think seek...get() and tell...get()) In contrast, seekp() and tellp() are associated with the write position in the buffer. (think seek...put() and tell...put())
An additional change to the final output of the example will make that clear. Change only the line associated with /* output results */ to:
/* output result */
std::cout << "\nstring: " << in.str() << "\ntellg : " << in.tellg()
<< "\ntellp : " << in.tellp() << '\n';
Changes to Output
The output now reveals the independent offsets reported by in.tellg() and in.tellp() after the final writes to in, e.g.
...
string: Hello, big wide world!
tellg : 6
tellp : 22
Where in.tellg() reports the position in the buffer where the next read (such as in >> word1;) will start from, while in.tellp() reports the position in the buffer where the next write will take place, (such as in << " ... of code";). After adding in << " ... of code";, outputting from the current tellg() position with std::cout << '\n' << in.rdbuf() << '\n'; would result in:
big wide world! ... of code
Look things over and let me know if you have further questions.
std::string word;
std::stringstream ss;
while (word != "n") {
std::cout << "Enter a word, ( n to stop) : ";
std::cin >> word;
std::cin.ignore();
if (word == "n")
ss << std::endl;
else {
ss << word;
ss << " ";
}
}
std::cout << "The content in stringstream is : " << ss.str() << std::endl;
std::cout << "The StreamPositionPosition is: "
<< ss.tellp() << std::endl; // Yes this is correct, it will give you the position of the current position where you are outputting.
// how should i open the stream to get input from it to store it in a variable?
// If you want to convert it to string simply
std::string str = ss.str();
// If you know the order of the data, you can simply extract it
std::string wordOutput;
ss >> wordOutput;
/* how should I switch the position pointer to a position when I use an
input/output stream like sstream with seekg(), seekp(), tellg(),tellp() */
/*
seekg sets the position of input
tellg gets the position of input
seekp sets the position of output
tellp gets the position of output
*/
long length = s.tellp(); // gives you the length of the string stream
// If you want the first position do,
ss.seekp(0);
ss >> wordOutput;
// If you want the third last, do
ss.seekp(length - 3);
ss >> wordOutput;
// Examples for inputs
stringstream example;
example.write ("This is an apple",16);
long pos = example.tellp();
example.seekp (pos-7);
example.write (" sam",4);
// Will result in This is a sample
The text to the left and right of the integer is relevant to initialize an object, so it needs to be kept. The starting of the integer will persist and i'm thinking I will try and use a while loop to check to see if the next character is also digit, but I feel like their must be a more elegant solution. Does anyone have any hints, tips, or tricks?
Instead of writing a while loop to convert a sequence of charachers to an integer value, the standard library provides std::istringstream and formatted input (operator>>() as illustrated by this simple example:
void example1()
{
std::string s{"Hello 1234 World\n"};
std::istringstream ss(s.substr(6,std::string::npos));
int nbr;
ss >> nbr;
std::cout << "The number is " << nbr << std::endl;
}
In this example, the starting position of the string is known. If not, you would need to either parse the string (which in simple cases can be done using similar techniques). For instance, if the number is preceeded by the string "Nbr:", you can use string::find
void example2()
{
std::string s{"Some uninteresting text... Nbr: 1234 World\n"};
auto pos = s.find("Nbr:");
std::istringstream ss(s.substr(pos+4,std::string::npos));
int nbr;
ss >> nbr;
std::cout << "The number is " << nbr << std::endl;
}
Or you can used regex to find the first number in the string and use std::stoi on the submatch:
void example3()
{
std::string s{"Some uninteresting text... : 1234 World\n"};
std::regex rgx("[^0-9]*(\\d+).*");
std::smatch match;
if (std::regex_search(s, match, rgx)) {
auto n = std::stoi(match[1]);
std::cout << "the number is " << n << '\n';
} else {
std::cout << "no match\n";
}
}
I am using strtod() to convert string to decimal. Since I need to throw an error for incorrect input/invalid characters in i have no other choice.
However the problem is that strtod() is affected by locales. So a '.' becomes an invalid character when the program is run in a different locale
If I use a solution like this:
std::istringstream text(iterator->second.c_str());
text.imbue(std::locale::classic());
double result;
text >> result;
it is not possible to check for invalid input at all.
Which solution can provide me both?
Yes you can determine if the value provided to the stringstream was completely converted or not. You can test the stream to see if it reached the end of file. If it has then you have read everything and converted it to a double. If not then there was invalid input.
Here is a little sample demonstrating the conversion:
bool convert(const std::string & text)
{
if (text = "")
return false;
std::istringstream iss(text);
iss.imbue(std::locale::classic());
double result;
iss >> result;
if (iss.eof())
return true;
else
return false;
}
int main()
{
std::cout << convert("123.456") << std::endl;
std::cout << convert("123.456abc") << std::endl;
std::cout << convert("123.456e5") << std::endl;
std::cout << convert("e5") << std::endl;
return 0;
}
Output:
1
0
1
0
Live Example
You can check for invalid input by testing text for errors and that it reached the end of input:
text >> result;
bool ok = text && text.eof();
Example: http://coliru.stacked-crooked.com/a/758b769f26567d1e
I'm trying to create a replacement for sprintfs' %05d behavior. Althought, I've found a similar question here on stackoverflow, the proposed solution there doesn't work for me when I'm testing with negative numbers.
Doing a "sprintf(buf, "%05d", -12)", I'm getting "-0012" back which looks nice.
Using stringstream, width and fill, I'm getting "00-12" which seams reasonable when looking at the docs from std::basic_ios::fill
The fill character is the character used by output insertion functions to fill spaces when padding results to the field width.
but doesn't look like something one could wish.
So I'm confused and don't know if I'm doing something obvious wrong or if width/fill from the std streams doesn't easily support this situation.
A compilable testcode can be found on codepad.
Here's an extract of the stream based conversion:
std::string NumberToString(const long iVal, const int iNumDigit)
{
std::stringstream ss;
if (iNumDigit >= 0) ss.fill(' ');
else if (iNumDigit < 0) ss.fill('0');
ss.width(std::abs(iNumDigit));
ss << iVal;
return ss.str();
}
EDIT1: Solution:
To match the std stream approach with printf formatting for %05d, jrok's solution can be used for the case with leading zeros. Here's the new function:
std::string NumberToString(const long iVal, const int iNumDigit)
{
std::stringstream ss;
if (iNumDigit >= 0) ss.fill(' ');
else if (iNumDigit < 0) { ss.fill('0'); ss.setf(std::ios::internal, std::ios::adjustfield); }
ss.width(std::abs(iNumDigit));
ss << iVal;
return ss.str();
}
Use stream manipulator std::internal.
It (along with std::left and std::right) lets you specify where the fill characters go. For example
std::cout << std::setw(5) << std::setfill('0') << std::internal << -1;
will print -0001.
I have tried to convert a color code by reading a file, retrieve the color code and store it as a string. This works, but when I tried to just simply convert it to an int, it doesn't work - always getting 0 when I do a cout.
string value = "0xFFFFFF";
unsigned int colorValue = atoi(value.c_str());
cout << colorValue << endl;
as you can see, the color I've got is 0xFFFFFF, but converting it to an int will only give me 0. Can someone please tell me what I'm missing or what i'm doing wrong?
Thanks
I suggest using stringstreams:
std::string value = "0xFFFFFF";
unsigned int colorValue;
std::stringstream sstream;
sstream << std::hex << value;
sstream >> colorValue;
cout << colorValue << endl;
As #BartekBanachewicz says, atoi() is NOT the C++ way of doing this. Leverage the power of C++ streams and use std::istringstream to do it for you. See this.
An excerpt:
template <typename DataType>
DataType convertFromString(std::string MyString)
{
DataType retValue;
std::stringstream stream;
stream << std::hex << MyString; // Credit to #elusive :)
stream >> retValue;
return retValue;
}