11

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;
}
Southern Hospitality
  • 1,260
  • 1
  • 8
  • 12
  • read/readsome only operate on char *s and you cannot get a char * (only a const char *) from a string type, so you can't read into a string type directly with them. – Southern Hospitality Aug 22 '10 at 04:47

3 Answers3

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) );
ngoozeff
  • 4,576
  • 3
  • 28
  • 21
  • byte waster! char buffer[128]; ifs.read(buffer,128); std::string first128(buffer,128); But really I want to do it as purely as possible. Answers with no angle brackets need not apply! – Southern Hospitality Aug 22 '10 at 04:45
  • @Southern Hospitality: I've edited to include another version. – ngoozeff Aug 22 '10 at 05:20
  • I think the first answer is perfect. You want to go into a string, just create a std::string of size 128 and pass that as the buffer to read. Thinking that isn't pure is stating that the standard library is somehow impure. – dash-tom-bang Aug 22 '10 at 05:42
  • Just FWIW, C++0x has a `copy_n` in the standard library, so you could soon eliminate that part of the second version. – Jerry Coffin Aug 22 '10 at 06:16
  • dash-tom-bang, you can't pass a std::string in where a char * is required, only where a const char * is required. Thanks for the 2nd effort, ngoozeff. – Southern Hospitality Aug 22 '10 at 07:03
  • @Jerry: thanks for pointing that out. Looks like boost has a version aswell at 'boost/range/algorithm_ext/copy_n.hpp', although I haven't checked if it is a drop-in for the version I posted. In practice you would use the boost or C++0x version. – ngoozeff Aug 22 '10 at 07:40
  • @ngoozeff: I'm pretty sure either the Boost or the C++0x version is a drop-in replacement -- the all seem to have the parameters in the same order in any case. – Jerry Coffin Aug 22 '10 at 14:23
1

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.

Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • Will std::string provide write access to a contiguous buffer in C++0x? – Brent Bradburn Dec 11 '10 at 18:11
  • vector of char for buffer is veeeery bad idea, very ineffective. – Alecs Dec 08 '12 at 23:02
  • @Alecs: Could you elaborate a little? Do you mean that it won't work, or just that it is not the most efficient method? – Brent Bradburn Dec 08 '12 at 23:24
  • It will work, but it's not only not the most efficient method, but one of the most inefficient. Plain char is very efficient, if you are afraid to use them. better choose std::string or some other string class realization. But vector of char is some perversion – Alecs Dec 08 '12 at 23:30
  • 1
    @Alecs: I never knew that vector was a perversion. Do you have any reference for that? I think vector and string are likely to have the same storage footprint (especially for a power-of-2 allocation). Besides, I'm only advocating allocation of a single vector -- how bad could it be? I use vector frequently for this kind of thing, so I would like to know what you are talking about. – Brent Bradburn Dec 08 '12 at 23:52
  • hm, I googled that, and seems that many people use vector, so seems that thinking of that as perversion is just common for my circle. But anyway pure chars are much faster and uses less memory. – Alecs Dec 09 '12 at 10:52
-1

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.

ecoretchi
  • 127
  • 1
  • 3