Thursday, 11 March 2010

Joining a Vector And Splitting String In C++

We need these two opposite operations more or less often. As C++ stl does not provide us these functions, we need to write them ourselves.

template <class T>
string vector_join( const vector<T>& v, const string& token ){
  ostringstream result;
  for (typename vector<T>::const_iterator i = v.begin(); i != v.end(); i++){
    if (i != v.begin()) result << token;
    result << *i;
  }
  return result.str();
}

vector<string> string_split( const string& s, const string& delimiter ){
  vector<string> result;
  string::size_type from = 0;
  string::size_type to = 0;

  while ( to != string::npos ){
    to = s.find( delimiter, from );
    if ( from < s.size() && from != to ){
      result.push_back( s.substr( from, to - from ) );
    }
    from = to + delimiter.size();
  }
  return result;
}

The functions produce the following results:

string ss[] = { "qwerty", "hello", "world" };
vector<string> v( ss, ss+3 );
vector_join( v, ", ") );//"qwerty, hello, world"

int ii[] = { 111, 222, 333 };
vector<int> vi( ii, ii+3 );
vector_join( vi, "; ") );//"111; 222; 333"

string_split( "qwerty, hello, world", ", " );
//results in [ "qwerty", "hello", "world" ]
string_split( ", , wow, , qwerty, hello, world, ", ", " );
//results in [ "wow", "qwerty", "hello", "world" ]

If we have a space as delimiter, we can split string more efficient way and even make use of templates too

template <class T>
vector<T> string_split( const string& s ){
  vector<T> result;
  istringstream ss(s);
  copy( istream_iterator<T>(ss), istream_iterator<T>(), 
        back_inserter( result ) );
  return result;
}

And the tests:

string_split<float>( "11.1 22.2 33.3" );//[ 11.1, 22.2, 33.3 ]
string_split<int>( "111 222 333" );//[ 111, 222, 333 ]