6

If I want to copy the contents of a file to a vector, I can do it like that:

std::ifstream file("path_to_file");
std::vector<char> buffer(std::istream_iterator<char>(file), 
                         std::istream_iterator<char>());

My question is, how would I do this if I want to copy only the first n chars?

Edit I could write my own version of copy, but is there a way to do this using only existing components?

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283

5 Answers5

8

As Alexander points out, the fastest way would be

std::vector<char> buffer(n);
file.read(&buffer[0], n);

In C++0x, you can use buffer.data() instead of &buffer[0]; the latter has undefined behavior if n == 0.

avakar
  • 32,009
  • 9
  • 68
  • 103
6

As was noted by Steve, this would need copy_n(), which, due to an oversight, isn't in the current standard library, but will be in C++1x. You can implement one yourself easily, here's one I believe to be correct:

template<class InIt, class OutIt> 
OutIt copy_n(InIt src, OutIt dest, size_t n) 
{
  if (!n) return dest;
  *dest = *src;
  while (--n)
    *++dest = *++src;
  return ++dest; 
} 

Note that std::copy_n() presumes the input iterator to be able to deliver n objects. When reading from a file, this could be problematic.


Absent of std::copy_n(), you could use std::generate_n.

template< typename InIt >
struct input_generator {
  typedef std::iterator_traits<InIt>::value_type value_type;

  input_generator(InIt begin, InIt end) begin_(begin), end_(end) {}

  value_type operator()()
  {
    assert(it_ != end);
    return *it_++;
  }

  Init begin_;
  Init end_;
};


std::vector<char> buffer;
buffer.reserve(42);

std::generate_n( std::back_inserter(buffer)
               , 42
               , input_generator(std::istream_iterator<char>(file))
               , input_generator(std::istream_iterator<char>()) );

However, I don't see this as an advantage over reading directly from the file as avakar showed.

Community
  • 1
  • 1
sbi
  • 219,715
  • 46
  • 258
  • 445
  • Sbi's implementation was not correct for stream iterators, which read from the stream on both increment and initialization. I fixed it. – Don Reba Jan 19 '12 at 10:25
  • @Don: Thanks. That's the reason why even such simple algorithms should be in the std lab — it's easy to let subtle bugs slip even into the most simple ones. – sbi Jan 19 '12 at 11:19
1

The "STL way" is to use copy_n, which is in the STL but not the C++ standard.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Sounds reasonable, but not advisable if portability is a concern. Also, is this added to the standard in C++x0? – Björn Pollex Sep 30 '10 at 12:12
  • Yes, it's in C++0x. If portability is a concern, then don't do things the STL way, do them the C++ standard way ;-) In this case that probably means just implement copy_n yourself, in your own namespace, perhaps by taking an implementation from somewhere else. – Steve Jessop Sep 30 '10 at 12:46
0
char *buffer = new char[n];
file.read(buffer,n);
std::vector<char> myVector
for (int i=0;i<n;i++) myVector.push_back(buffer[i]);
delete [] buffer;

// myVector now holds the first n bytes of file.

It may not be the prettiest or quickest way, but it is how I would do it.

Alexander Rafferty
  • 6,134
  • 4
  • 33
  • 55
0

Credit due to @sbi, I forgot about generate_n.

The streambuf inside file has a function snextc which returns the next character, qualifying it as a generator function, once its implicit this argument is bound.

generate_n( back_inserter( buffer ), 42,
            tr1::bind( tr1::mem_fn( &streambuf::snextc ), file.rdbuf() ) );

For vector, just read directly. This is good for deque or whatever.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421