Jonathan Mee
Jonathan Mee

Reputation: 38919

Is There a Standard Algorithm to Copy Until?

I am using an istream_iterator<char> it, so I cannot iterate over the range in reverse (or iterate over it twice, without a great deal of hastle.)

I want to copy until a condition is met. Is there something that would work like this in the standard library:

copy_until(it, istream_iterator<char>(), ostream_iterator<char>(cout), [](const unsigned char i){ return isalpha(i); })

If I have to roll something I can I was just hoping for some magic that I haven't been able to figure out.

EDIT:

The behavior I would expect from my made up copy_until function is:

while(it != istream_iterator<char>()) {
    if(!isalpha(static_cast<unsigned char>(*it))) break;
    cout << *it++;
}

Upvotes: 5

Views: 2039

Answers (4)

blubase
blubase

Reputation: 845

copy_until could be realized with std::find_if, and std::copy.

Using the same structure as redleg, the algorithm could first search for the terminator, and then copy the range.

template <class _InIt, class _OutIt, class _Pr>
inline void copy_until(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred) {
    _InIt _posTerm = std::find_if(_First, _Last, _Pred);
    std::copy(_First, _posTerm, _Dest);
}

The template requires <algorithm>, and <iterator>.

Here is an example how to use it:

std::vector<int> source = {1,2,3,4,5,6,7,8};
std::vector<int> dest {};

copy_until(source.begin(),source.end(), 
           back_inserter(dest),[](int c) {return c == 5;});

with the output:

source: 1 2 3 4 5 6 7 8 
dest  : 1 2 3 4 

Beacuse std::copy is defined to copy till last, but excluding last or in other terms [first,last), the position of find_if is not copied.

In case you need an inclusive until, meaning [first,last], we need to do some more checking. If the Terminator is at the end() the iterator should not be advanced.

template <class _InIt, class _OutIt, class _Pr>
inline void copy_until(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred)
{
    _InIt _posTerm = std::find_if(_First, _Last, _Pred);
    if (_posTerm != _Last ) { _posTerm++; }
    std::copy(_First,_posTerm,_Dest);
}

the previous example would now output:

source: 1 2 3 4 5 6 7 8 
dest  : 1 2 3 4 5

Upvotes: -1

redleg
redleg

Reputation: 317

Just for completness, since the standard provides no out of box solution, thats my solution:

template<class _InIt, class _OutIt, class _Pr>
inline void copy_until (_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred) {
  while ((_First != _Last) && _Pred(*_First)) {
    *_Dest++ = *_First++;
  }
}

And that's how I use it:

copy_until(std::istreambuf_iterator<char>(is),
           std::istreambuf_iterator<char>(),
           std::ostreambuf_iterator<char>(os), 
           [] (char c) { return <some usefull condition here> });

For instance to read a string with only alnum characters from an input stream:

std::istream& operator>> (std::istream& is, std::string& n) {
  std::ostringstream str;
  copy_until(std::istreambuf_iterator<char>(is),
             std::istreambuf_iterator<char>(),
             std::ostreambuf_iterator<char>(str), 
             std::isalnum);
  n = str.str();
  return is;
}

Upvotes: 9

Jonathan Mee
Jonathan Mee

Reputation: 38919

http://en.cppreference.com/w/cpp/algorithm Provides a very helpful reference to all the algorithms available in C++ (not just those in the Algorithm Library but also the Numeric, Memory, and CStd Libraries.) Of those the following are copying algorithms, that is they take Input Iterator(s), Output Iterator(s), and lambda(s) as arguments:

  • copy_if "Copies the elements in the range, defined by [first, last)... Only copies the elements for which the predicate pred returns true"
  • transform "Applies the given function to a range and stores the result in another range."
  • remove_copy_if "Copies elements from the range [first, last), to another range beginning at d_first, omitting the elements which satisfy specific criteria"
  • replace_copy_if "Copies the all elements from the range [first, last) to another range beginning at d_first replacing all elements satisfying specific criteria with new_value"
  • unique_copy "Copies the elements from the range [first, last), to another range beginning at d_first in such a way that there are no consecutive equal elements... Elements are compared using the given binary predicate p"
  • partition_copy "Copies the elements from the range [first, last) to two different ranges depending on the value returned by the predicate p. The elements, that satisfy the predicate p, are copied to the range beginning at d_first_true. The rest of the elements are copied to the range beginning at d_first_false"
  • merge Requires a 2nd input range
  • set_difference Requires a 2nd input range
  • set_intersection Requires a 2nd input range
  • set_symmetric_difference Requires a 2nd input range
  • set_union Requires a 2nd input range
  • adjacent_difference "Computes the differences between the second and the first of each adjacent pair of elements of the range [first, last)... Differences are calculated using the given binary function op"
  • partial_sum "Computes the partial sums of the elements in the subranges of the range [first, last) and writes them to the range beginning at d_first... To sum up the elements, the second version uses the given binary function op."
  • exclusive_scan "Computes an exclusive prefix sum operation using binary_op for the range [first, last)"
  • inclusive_scan "Computes an inclusive prefix sum operation using binary_op for the range [first, last)"
  • transform_exclusive_scan "Transforms each element in the range [first, last) with unary_op, then computes an exclusive prefix sum operation using binary_op over the resulting range"
  • transform_inclusive_scan "Transforms each element in the range [first, last) with unary_op, then computes an inclusive prefix sum operation using binary_op over the resulting range"

Because the lambda is only used to modify the 1:1 assignment of the range [first, last) to d_first; transform, replace_copy_if, and all of the Numeric Library algorithms are unhelpful (adjacent_difference, partial_sum, exclusive_scan, inclusive_scan, transform_exclusive_scan, and transform_inclusive_scan.)

  • If after the lambda condition was met the remainder of the range [it, istream_iterator<char>()) was to be directly copped to a 2nd output iterator, partition_copy would solve your problem
  • If after the lambda condition was met the remainder of the range [it, istream_iterator<char>()) was to be iterated over by a function, this function could be called on each value after the condition was met by copy_if (or remove_copy_if or unique_copy)
  • But in the general case, the answer to your question is the standard algorithms do not provide a "copy_until" so you'll need to use your while-loop

Upvotes: 1

SergeyA
SergeyA

Reputation: 62563

There is no copy until out of the box. Since you are copying from istream there is no other option but to use the loop with the break statement in it.

Upvotes: 3

Related Questions