Updated string split method to accept limitParts and ignoreEmpty options.

This commit is contained in:
Patrick 2024-08-18 17:24:06 +02:00
parent d508ccfe2b
commit 03c899f17e

View File

@ -5,7 +5,7 @@
#define MIJIN_UTIL_STRING_HPP_INCLUDED 1 #define MIJIN_UTIL_STRING_HPP_INCLUDED 1
#include <iterator> #include <iterator>
#include <ranges> #include <limits>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -28,6 +28,12 @@ namespace mijin
// public types // public types
// //
struct SplitOptions
{
std::size_t limitParts = std::numeric_limits<std::size_t>::max();
bool ignoreEmpty = true;
};
// //
// public functions // public functions
// //
@ -56,11 +62,74 @@ namespace detail
{ {
template<typename TChar, typename TTraits> template<typename TChar, typename TTraits>
std::vector<std::basic_string_view<TChar, TTraits>> splitImpl(std::basic_string_view<TChar, TTraits> stringView, std::vector<std::basic_string_view<TChar, TTraits>> splitImpl(std::basic_string_view<TChar, TTraits> stringView,
std::basic_string_view<TChar, TTraits> seperator) std::basic_string_view<TChar, TTraits> separator,
const SplitOptions& options)
{ {
return std::views::split(stringView, seperator) using sv_t = std::basic_string_view<TChar, TTraits>;
| std::views::transform([](auto val) { return std::string_view(val); })
| std::ranges::to<std::vector>(); MIJIN_ASSERT(options.limitParts > 0, "Cannot split to zero parts.");
MIJIN_ASSERT(!separator.empty(), "Separator cannot be empty.");
if (separator.empty())
{
return {};
}
std::vector<sv_t> result;
if (options.limitParts <= 1)
{
result.push_back(stringView);
return result;
}
auto start = stringView.begin();
auto pos = start;
const auto end = stringView.end();
auto seperatorFound = [&]()
{
return pos <= (end - separator.size()) && sv_t(pos, pos + separator.size()) == separator;
};
while (pos != end)
{
if (seperatorFound())
{
if (!options.ignoreEmpty || pos != start)
{
result.emplace_back(start, pos);
}
start = pos = (pos + separator.size());
if (result.size() == options.limitParts - 1)
{
if (options.ignoreEmpty)
{
while (seperatorFound())
{
pos += separator.size();
}
}
result.emplace_back(pos, end);
return result;
}
if (!options.ignoreEmpty && pos == end)
{
// skipped a separator at the very end, add an empty entry
result.emplace_back("");
return result;
}
}
else
{
++pos;
}
}
if (start != end)
{
result.emplace_back(start, end);
}
return result;
} }
template<typename TChar, typename TTraitsA, typename TTraitsB> template<typename TChar, typename TTraitsA, typename TTraitsB>
@ -87,9 +156,10 @@ static const TChar SPACE = TChar(' ');
} }
template<typename TLeft, typename TRight> template<typename TLeft, typename TRight>
[[nodiscard]] auto split(TLeft&& left, TRight&& right) [[nodiscard]] auto split(TLeft&& stringView, TRight&& separator, const SplitOptions& options = {})
{ {
return detail::splitImpl(std::basic_string_view(std::forward<TLeft>(left)), std::basic_string_view(std::forward<TRight>(right))); return detail::splitImpl(std::basic_string_view(std::forward<TLeft>(stringView)),
std::basic_string_view(std::forward<TRight>(separator)), options);
} }
template<typename TChar, typename TTraits> template<typename TChar, typename TTraits>