It is always a good idea to look up the functions you are using in the documentation. For example,
copy() says:
http://www.cplusplus.com/reference/algorithm/copy.html
it copies the entire range from the begin iterator to the end.
So there are two ways to fix it.
1) Give it a smaller input range,
2) Use an algorithm that stops before it hits the end.
We'll do both. (2) to
read input that we can play with. And (1) to
write output to file.
Since we are dealing with files and we want to stop on two conditions, a certain number of elements are copied or we hit the end of input, we'll write our own version of the copy() function that adds the maximum number of elements to copy as an argument. (Might as well make it a nice general-purpose algorithm that can be used elsewhere too.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
//--------------------------------------------------------------------------
// copy_max()
// Copy a maximum number of elements in the input range to the
// destination.
//
// returns
// result +num_elements_copied
//
template <typename InputIterator, typename SizeType, typename OutputIterator>
inline OutputIterator copy_max(
InputIterator first,
InputIterator end,
SizeType count,
OutputIterator result
) {
for (; (count > 0) && (first != end); --count)
*result++ = *first++;
return result;
}
|
I suppose we could abuse some other STL algorithm to do it, but this makes for more readable code with zero increase in bloat (well, except for about 20 lines of source code).
Now we have all we need to get a block of input.
1 2 3 4 5 6 7
|
string s;
copy_max(
istream_iterator <unsigned char> ( inf ),
istream_iterator <unsigned char> (),
125,
back_insert_iterator <string> ( s )
);
|
At this point, we can do whatever we like with
s and write stuff to
outf.
1 2 3 4 5 6 7
|
outf << "The following block contains the value 65h (ASCII 'e') "
<< std::count(
s.begin(),
s.end(),
(char)0x65
)
<< " times.\n";
|
When we're done messing with it, we can do our thing like before:
1 2 3 4 5 6 7
|
istringstream iss( s );
iss >> noskipws;
copy(
istream_iterator <unsigned char> ( iss ),
istream_iterator <unsigned char> (),
ostream_iterator <hex_type> ( outf )
);
|
The
hex_type was designed just for this one program, not for general use. You may have noticed it has a magic number on line 30.
if (++count > 25)
I chose to line the output in rows of 26 just because it fit nicely in 80-columns. But if you're outputting 125 characters at a time, it will look just a little funny. So you might as well change it to 25 items per row, so you'll have a nice square block of (125/25) five rows. Change that
25
to
24
.
Finally, you want everything in a loop, so you can do it for your entire input. However, there is one caveat you need to be aware of. (It stymied me for a while.) There is a bug in the way the
istream_iterator indexes the input between multiple references. (Confirmed both for
GCC and
Borland C++Builder 2006, and presumed the standard works the same with other compilers too.)
Fortunately, since we are playing with a file, we've got nice random-access powers that we might as well put to work and fix it. Our loop:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
// While there's input, get sections.
// The last section may not be fully populated.
for (unsigned section_number = 0; inf; section_number++)
{
// Handle the stupid istream_iterator bug: goto the next block to read.
inf.seekg( section_number *125 );
// Get the block of input
string s;
copy_max(
istream_iterator <unsigned char> ( inf ),
istream_iterator <unsigned char> (),
125,
back_insert_iterator <string> ( s )
);
// Play with it
outf << "Section "
<< (section_number +1)
<< " contains the value 65h (ASCII 'e') "
<< std::count(
s.begin(),
s.end(),
(char)0x65
)
<< " times.\n";
// And write our pretty rows
istringstream iss( s );
iss >> noskipws;
copy(
istream_iterator <unsigned char> ( iss ),
istream_iterator <unsigned char> (),
ostream_iterator <hex_type> ( outf )
);
outf << endl;
}
|
Whew. Hope this helps. :-)