This question already has an answer here:
-
Reading directly from an std::istream into an std::string
6 answers
How do I read exactly 128 bytes from an fstream into a string object?
I wrote some code to read the first 128 bytes of a file and print it and then the last 128 bytes of the file and print that. The last part works, since you can easily iterate to EOF, but how do I get exactly 128 bytes from the front? The code below doesn't work since you can't add 128 to an ifstream iterator, it's not indexable, only incrementable (it seems).
Sure I could make an iterator and *++ it 128 times, but there must be a single line straightforward way to do it, right?
#include <iostream>
#include <fstream>
#include <string>
int main(int argc, char **argv)
{
std::ifstream ifs ("input.txt",std::ifstream::in | std::ifstream::binary);
if (ifs.good())
{
// read first 128 bytes into a string
ifs.seekg(0,std::ifstream::beg);
std::string first128((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>(ifs))+128);
std::cout << first128 << std::endl;
// read last 128 bytes into a string
ifs.seekg(-128,std::ifstream::end);
std::string last128((std::istreambuf_iterator<char>(ifs)),
std::istreambuf_iterator<char>());
std::cout << last128 << std::endl;
return 0;
}
return 1;
}
char buffer[129];
ifs.read (buffer,128);
buffer[128] = '\0';
first128 = buffer;
How about this then:
template <typename Itr, typename Out>
void copy_n(Itr it, size_t count, Out out)
{
for(size_t i=0;i<count;++i)
out = *it++;
}
...
std::string first128;
std::istreambuf_iterator<char> it(ifs);
copy_n( it, 128,
std::back_inserter<std::string>(first128) );
My answer uses an intermediate buffer, but perhaps you will be happy that it uses iterators to initialize the string from the buffer.
std::vector<char> buffer(128); // create a buffer
ifs.read( &buffer[0], buffer.size() ); // read to buffer
std::string first128( buffer.begin(), buffer.end() ); // copy from vector
To me it seems like they got a little too cute with the implementation of iostreams. Trying to use iterators for stream I/O is overly complicated.
By the way, I suspect the implementation that you were attempting will, under the covers, do a variety of intermediate buffering (perhaps some in the kernel, some in the library) as well as re-allocating and copying the string several times as it grows.
One other idea: Do you really need the result in a standard string? You might just work from the vector -- avoiding the final step of copying to a string. Or, if you are feeling adventurous, you could create your own string class that does allow you to expose the internal buffer in the same way that vector does.
Here i have some of researching over streambuffer, read directly to string from istream by constructor:
class mystringbuf : public std::stringbuf
{
public:
explicit mystringbuf(std::istream& istr, size_t n,
std::ios_base::openmode __mode = std::ios_base::in )
{
_M_string.resize(n);
std::stringbuf::_M_stringbuf_init(__mode);
istr.read(gptr(), n);
}
public:
std::stringbuf::char_type* gptr() const
{
return std::stringbuf::gptr();
}
std::string& str_ref(){
return _M_string;
}
};
std::ostream& operator << (std::ostream& ostr, mystringbuf& buf){
ostr << buf.str_ref();
return ostr;
}
Example to use:
using std::cout;
using std::endl;
int main()
{
std::stringbuf buffer; // empty buffer
buffer.str("abc def ABC DEF "); // not empty now
std::istream is (&buffer); // associate stream buffer to stream
mystringbuf data(is, 10); // read 10 bytes
cout << "data=" << data << endl;
return 0;
}
Output:
data=abc def AB
Please forward me if i`am somewhere was wrong.