4.8. Joining a Sequence of Strings

Problem

Given a sequence of strings, such as output from Example 4-10, you want to join them together into a single, long string, perhaps with a delimiter.

Solution

Loop through the sequence and append each string to the output string. You can handle any standard sequence as input; Example 4-13 uses a vector of strings.

Example 4-13. Join a sequence of strings

#include <string>
#include <vector>
#include <iostream>

using namespace std;

void join(const vector<string>& v, char c, string& s) {

   s.clear();

   for (vector<string>::const_iterator p = v.begin();
        p != v.end(); ++p) {
      s += *p;
      if (p != v.end() - 1)
        s += c;
   }
}

int main() {

   vector<string> v;
   vector<string> v2;
   string s;

   v.push_back(string("fee"));
   v.push_back(string("fi"));
   v.push_back(string("foe"));
   v.push_back(string("fum"));

   join(v, '/', s);

   cout << s << '\n';
}

Discussion

Example 4-13 has one technique that is slightly different from previous examples. Look at this line:

for (vector<string>::const_iterator p = v.begin();

The previous string examples simply used iterators, without the “const” part, but you can’t get away with that here because v is declared as a reference to a const object. If you have a const container object, you can only use a const_iterator to access its elements. This is because a plain iterator allows writes to the object it refers to, which, of course, you can’t do if your container object is const.

I declared v const for two reasons. First, I know I’m not going to be modifying its contents, so I want the compiler to give me an error if I do. The compiler is much better at spotting that kind of thing than I am, especially since a subtle syntactic or semantic error can cause an unwanted assignment. Second, I want to advertise to consumers of this function that I won’t do anything to their container, and const is the perfect way to do that. Now, I just have to create a generic version that works on multiple character types.

Just as in Recipe 4.6, making join generic with a function template is easy. All you have to do is change the header to be parameterized on the type of character, like this:

template<typename T>
void joing(const std::vector<std::basic_string<T> >& v, T c,
           std::basic_string<T>& s)

But vectors may not be your only input. You may be saddled with the task of joining an array of C-style strings. C++ strings are preferable to C-style strings, so if you have to do this, join them into a C++ string. Once you’ve done that, you can always retrieve a C-style version by calling string’s c_str member function, which returns a const pointer to a null-terminated character array.

Example 4-14 offers a generic version of join that joins an array of character arrays into a string. Since the new, generic version is parameterized on the character type, it will work for narrow or wide character arrays.

Example 4-14. Joining C-style strings

#include <string>
#include <iostream>

const static int MAGIC_NUMBER = 4;

template<typename T>
void join(T* arr[], size_t n, T c, std::basic_string<T>& s) {
   s.clear();

   for (int i = 0; i < n; ++i) {
      if (arr[i] != NULL)
         s += arr[i];
      if (i < n-1)
         s += c;
   }
}

int main() {
   std::wstring ws;

   wchar_t* arr[MAGIC_NUMBER];

   arr[0] = L"you";
   arr[1] = L"ate";
   arr[2] = L"my";
   arr[3] = L"breakfast";

   join(arr, MAGIC_NUMBER, L'/', ws);


}

Get C++ Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.