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 #include #include using namespace std; void join(const vector& v, char c, string& s) { s.clear( ); for (vector::const_iterator p = v.begin( ); p != v.end( ); ++p) { s += *p; if (p != v.end( ) - 1) s += c; } } int main( ) { vector v; vector 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 << ' '; }
Discussion
Example 4-13 has one technique that is slightly different from previous examples. Look at this line:
for (vector::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 void joing(const std::vector >& v, T c, std::basic_string& 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 #include const static int MAGIC_NUMBER = 4; template void join(T* arr[], size_t n, T c, std::basic_string& 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); }