简体   繁体   中英

How to correctly get the file/stream position pointers in C++ for an input/output stream?

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 ie 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:

  1. 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;
  2. 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;
  3. 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, eg

    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, " , eg

    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 , eg

...
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 technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM