utils: improve split / join

This commit is contained in:
Dirk-Jan C. Binnema 2022-03-19 10:58:13 +02:00
parent 056fecd6aa
commit e0096c3dee
3 changed files with 97 additions and 1 deletions

View File

@ -30,6 +30,7 @@
#include <string.h>
#include <iostream>
#include <algorithm>
#include <numeric>
#include <glib.h>
#include <glib/gprintf.h>
@ -196,7 +197,61 @@ Mu::split(const std::string& str, const std::string& sepa)
return vec;
}
std::vector<std::string>
Mu::split(const std::string& str, char sepa)
{
std::vector<std::string> vec;
size_t b = 0, e = 0;
/* special case */
if (str.empty())
return vec;
while (true) {
if (e = str.find(sepa, b); e != std::string::npos) {
vec.emplace_back(str.substr(b, e - b));
b = e + sizeof(sepa);
} else {
vec.emplace_back(str.substr(b));
break;
}
}
return vec;
}
std::string
Mu::join(const std::vector<std::string>& svec, const std::string& sepa)
{
if (svec.empty())
return {};
/* calculate the overall size beforehand, to avoid re-allocations. */
size_t value_len =
std::accumulate(svec.cbegin(), svec.cend(), 0,
[](size_t size, const std::string& s) {
return size + s.size();
}) + (svec.size() - 1) * sepa.length();
std::string value;
value.reserve(value_len);
std::accumulate(svec.cbegin(), svec.cend(), std::ref(value),
[&](std::string& s1, const std::string& s2)->std::string& {
if (s1.empty())
s1 = s2;
else {
s1.append(sepa);
s1.append(s2);
}
return s1;
});
return value;
}
std::string
Mu::quote(const std::string& str)

View File

@ -79,6 +79,32 @@ std::string remove_ctrl(const std::string& str);
*/
std::vector<std::string> split(const std::string& str, const std::string& sepa);
/**
* Split a string in parts. As a special case, splitting an empty string
* yields an empty vector (not a vector with a single empty element)
*
* @param str a string
* @param sepa the separator
*
* @return the parts.
*/
std::vector<std::string> split(const std::string& str, char sepa);
/**
* Join the strings in svec into a string, separated by sepa
*
* @param svec a string vector
* @param sepa separator
*
* @return string
*/
std::string join(const std::vector<std::string>& svec, const std::string& sepa);
static inline std::string join(const std::vector<std::string>& svec, char sepa) {
return join(svec, std::string(1, sepa));
}
/**
* Quote & escape a string for " and \
*

View File

@ -161,7 +161,6 @@ test_format()
}
static void
test_split()
{
using svec = std::vector<std::string>;
@ -178,8 +177,23 @@ test_split()
assert_equal_svec(split("ayybyyc", "yy"), {"a", "b", "c"});
assert_equal_svec(split("abc", ""), {"a", "b", "c"});
assert_equal_svec(split("", "boo"), {});
assert_equal_svec(split("axbxc", 'x'), {"a", "b", "c"});
assert_equal_svec(split("axbxcx", 'x'), {"a", "b", "c", ""});
assert_equal_svec(split("", "boo"), {});
}
static void
test_join()
{
assert_equal(join({"a", "b", "c"}, "x"), "axbxc");
assert_equal(join({"a", "b", "c"}, ""), "abc");
assert_equal(join({},"foo"), "");
assert_equal(join({"d", "e", "f"}, "foo"), "dfooefoof");
}
enum struct Bits { None = 0, Bit1 = 1 << 0, Bit2 = 1 << 1 };
MU_ENABLE_BITOPS(Bits);
@ -222,6 +236,7 @@ main(int argc, char* argv[])
g_test_add_func("/utils/clean", test_clean);
g_test_add_func("/utils/format", test_format);
g_test_add_func("/utils/split", test_split);
g_test_add_func("/utils/join", test_join);
g_test_add_func("/utils/define-bitmap", test_define_bitmap);
return g_test_run();