1

I need to test to see if the number of extracted strings from a string_view is equal to a specific number (e.g. 4) and then execute some code.

This is how I do it:

#include <iostream>
#include <iomanip>
#include <utility>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include <iterator>


int main( )
{
    const std::string_view sv { "   a 345353d&  ) " }; // a sample string literal

    std::stringstream ss;
    ss << sv;

    std::vector< std::string > foundTokens { std::istream_iterator< std::string >( ss ),
                                             std::istream_iterator< std::string >( ) };

    if ( foundTokens.size( ) == 4 )
    {
        // do some stuff here
    }

    for ( const auto& elem : foundTokens )
    {
        std::cout << std::quoted( elem ) << '\n';
    }
}

As can be seen, one of the downsides of the above code is that if the count is not equal to 4 then it means that the construction of foundTokens was totally wasteful because it won't be used later on in the code.

Is there a way to check the number of std::strings stored in ss and then if it is equal to a certain number, construct the vector?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
digito_evo
  • 3,216
  • 2
  • 14
  • 42
  • 1
    One approach to limit the amount of wasted effort would be something akin to the `count` argument to .NET's String.Split.... it won't return more than that number of elements. If you had a way to say "iterate up to the end but not more than 5 times" you could distinguish <4, ==4, and >4 and never waste time extracting a thousand strings. But I don't know of any standard algorithm that combines `std::copy`'s comparison to end iterator and `std::copy_n`'s counting. However there's [an existing question that addresses it](https://stackoverflow.com/q/26119212/103167) – Ben Voigt Mar 04 '22 at 22:32
  • 2
    Ahh, there's now a solution in the Standard: [`std::ranges::take_view`](https://en.cppreference.com/w/cpp/ranges/take_view) – Ben Voigt Mar 04 '22 at 22:49
  • @Ben Voigt Interesting. So how about efficiency? In one of the answers, it's said that it's not efficient to iterate over the `stringstream` and then construct the vector. How does `take_view` deal with it? – digito_evo Mar 04 '22 at 22:55
  • 2
    As I'm suggesting you call `views::take` with a count argument of 5, the vector constructor will make only a single pass, and it will stop after copying one string too many, at which point you can inspect the size, see >4, and bail out. You'll have wasted the limited effort of filling a vector with 5 strings, but you won't ever waste the effort of finding separators between a thousand strings (as `std:distance` would do) or copying all thousand strings into a vector (as your original code does) – Ben Voigt Mar 04 '22 at 22:59
  • @Ben Voigt Hmm... good idea. I'll try that. – digito_evo Mar 04 '22 at 23:03

2 Answers2

1

NO, a stringstream internally is just a sequence of characters, it has no knowledge of what structure the contained data may have. You could iterate the stringstream first and discover that structure but that wouldn't be any more efficient than simply extracting the strings.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
  • So should I keep it as it is? I mean construct the `vector` and then check its size? Is it really better than iterating over `ss` and then constructing the `vector` if needed? Also, do you think this function called [view](https://en.cppreference.com/w/cpp/io/basic_stringstream/view) can be of any use for this particular problem? – digito_evo Mar 04 '22 at 22:28
1

You can do it something like the following

    #include <iterator>

    //...

    std::istringstream is( ss.str() );
    auto n = std::distance( std::istream_iterator< std::string >( is ),
        std::istream_iterator< std::string >() );

After that comparing the value of the variable n you can decide whether to create the vector or not.

For example

std::vector< std::string > foundTokens;

if ( n == 4 ) foundTokens.assign( std::istream_iterator< std::string >( ss ), std::istream_iterator< std::string >( ) );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335