In this repo I've put together a header only string splitter, allowing for characters and string literals as delimiters.
The (little) library is strictly C++17.
I would like to ask for your comments.
I would also like the code to be as short as possible, so any comments in that direction are also most welcome.
As it appears to be mandatory to include at least 3 lines of code, here's the code:
// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <algorithm>
#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
std::ostream & nl ( std::ostream & out_ ) { return out_ << '\n'; }
std::wostream & nl ( std::wostream & out_ ) { return out_ << L'\n'; }
namespace sax::detail {
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( std::basic_string_view<CharT> x ) noexcept {
return x; // guaranteed copy elision.
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( CharT x ) noexcept {
return std::basic_string_view<CharT> ( std::addressof ( x ), 1 );
}
template<typename CharT>
[[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( const CharT * x ) noexcept {
return std::basic_string_view<CharT> ( x );
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( 0, x.size ( ), x ) == 0 ) {
s.remove_prefix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
if ( s.size ( ) >= 1 and s [ 0 ] == x ) {
s.remove_prefix ( 1 );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_prefix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_prefix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_prefix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {
// This bit will come with C++20.
if ( s.size ( ) >= x.size ( ) and s.compare ( s.size ( ) - x.size ( ), std::basic_string_view<CharT>::npos, x ) == 0 ) {
s.remove_suffix ( x.size ( ) );
removed = removed or true;
};
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( std::addressof ( x ), 1 ) );
}
template<typename CharT>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {
remove_suffix ( s, removed, std::basic_string_view<CharT> ( x ) );
}
template<typename CharT, typename ... Args>
constexpr void remove_suffix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
bool removed = false;
do {
removed = false;
( remove_suffix ( s_, removed, std::forward<Args> ( args_ ) ), ... );
} while ( removed ); // Keep removing untill nothing more can be removed.
}
template<typename CharT, typename SizeT, typename StringyThing>
constexpr void find ( std::basic_string_view<CharT> & s, SizeT & f_, StringyThing x_ ) noexcept {
f_ = std::min ( s.find ( make_string_view<CharT> ( x_ ) ), f_ );
}
template<typename CharT, typename ... Args>
[[ nodiscard ]] constexpr auto find ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {
auto found = std::basic_string_view<CharT>::npos;
( find ( s_, found, std::forward<Args> ( args_ ) ), ... );
return found;
}
}
namespace sax {
template<typename CharT, typename ... Delimiters>
[[ nodiscard ]] std::vector<std::basic_string_view<CharT>> string_split ( const std::basic_string<CharT> & string_, Delimiters ... delimiters_ ) {
using size_type = typename std::basic_string_view<CharT>::size_type;
std::basic_string_view<CharT> string_view ( string_ );
std::vector<std::basic_string_view<CharT>> string_view_vector;
string_view_vector.reserve ( 4 ); // Avoid small size re-allocating, 0 > 1 > 2 > 3 > 4 > 6, now 4 > 6 > 9 etc.
// Remove trailing delimiters.
detail::remove_suffix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
// Parse the string_view left to right.
while ( true ) {
detail::remove_prefix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
const size_type pos = detail::find ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );
if ( std::basic_string_view<CharT>::npos == pos ) {
string_view_vector.emplace_back ( std::move ( string_view ) );
break;
}
string_view_vector.emplace_back ( string_view.data ( ), pos );
string_view.remove_prefix ( pos );
}
return string_view_vector;
}
}
Used like so:
// MIT License
//
// Copyright (c) 2019 degski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <random>
#include <string>
#include <type_traits>
#include <vector>
namespace fs = std::filesystem;
#include <string_split.hpp>
template<typename Stream, typename Container>
Stream & operator << ( Stream & out_, const Container & s_ ) noexcept {
for ( const auto & v : s_ )
out_ << '\"' << v << "\" ";
out_ << '\b';
return out_;
}
int main ( ) {
std::string s ( " , \t the quick brown ,, ,fox jumps underover \t , the lazy dog ," );
std::cout << sax::string_split ( s, " ", ',', "\t", "under" ) << nl;
return EXIT_SUCCESS;
}
Output:
"the" "quick" "brown" "fox" "jumps" "over" "the" "lazy" "dog"
What the code does (is supposed to do): Remove from the string any delimiters passed in, doing that left to right, applying the delimiters left to right and return the now separate bits left as a vector of string_view's over the original string. The latter means the string has to outlive the vector of string views.
The above does means that depending on what kind of delimiters you put (as they can be strings, which can interact with each other), that the order of the delimiters has significance.
The nl-code was originally in another header part of a bigger little lib, but due to the requirement all should be self-contained is just thrown in there.
removed = removed or true
can/should be removed = true
.
make_string_view ( CharT x )
returning a pointer to a temporary (the argument)?removed = removed or true
should just beremoved = true
,nl
doesn't belong in the header as far as I can tell, and I'm pretty sure it can be made smaller, but it's a bit hard to comment more specifically without a set of test-cases and/or description of what's expected to be returned in various corner cases. \$\endgroup\$StrSplit()
)? \$\endgroup\$