views:

160

answers:

3

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;
}
+1  A: 
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
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
@Southern Hospitality: I've edited to include another version.
ngoozeff
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
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
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
@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
@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
+2  A: 

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.

nobar
+1  A: 

I think that the following related question has the answer you're seeking.

celavek
Pretty good answer, although the answer is seemingly not quite defined to work by the standard. Even though it does work in the case I tried.
Southern Hospitality
Here: http://herbsutter.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/ is an article about std vectors being contiguous; see a user's question about std::string and read Herb's answer.
celavek
@Southern: it's being clarified by the new version of the standard. It would not be easy to find an implementation where this doesn't work. Here is a direct link to what Marius is referring to: http://herbsutter.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/#comment-483
Potatoswatter