diff --git a/include/daw/daw_string_view.h b/include/daw/daw_string_view.h index 286b19147..9af1d4155 100644 --- a/include/daw/daw_string_view.h +++ b/include/daw/daw_string_view.h @@ -1379,6 +1379,45 @@ namespace daw { return result; } + /// @brief Searches backwards for the first non-match of where, returns + /// the matching suffix, then removes it from the string_view + /// @param where The character to extract + /// @return substring after the last non-matching character + [[nodiscard]] constexpr basic_string_view pop_back_while( CharT where ) { + auto const pos = find_last_not_of( where ); + auto const count = + pos == npos ? size( ) : static_cast( size( ) - pos - 1U ); + return pop_back( count ); + } + + /// @brief Searches backwards for the first character not in where, + /// returns the matching suffix, then removes it from the string_view + /// @param where The characters to extract + /// @return substring after the last non-matching character + [[nodiscard]] constexpr basic_string_view + pop_back_while( basic_string_view where ) { + auto const pos = find_last_not_of( where ); + auto const count = + pos == npos ? size( ) : static_cast( size( ) - pos - 1U ); + return pop_back( count ); + } + + /// @brief Searches backwards until pred is false, returns the matching + /// suffix, then removes it from the string_view + /// @tparam UnaryPredicate A predicate taking a single CharT + /// @param pred Predicate identifying characters to extract + /// @return substring after the last character where pred is false + template + DAW_REQ_UNARY_PRED_REQ( UnaryPredicate, CharT ) + [[nodiscard]] constexpr basic_string_view + pop_back_while( UnaryPredicate pred ) { + auto const pos = find_last_not_of_if( std::move( pred ) ); + auto const count = + pos == npos ? size( ) : static_cast( size( ) - pos - 1U ); + return pop_back( count ); + } + /// @brief searches for last where, returns substring between /// where and end, then pops off the substring /// @param where string to split on and remove from back @@ -1798,6 +1837,80 @@ namespace daw { return *this; } + /// @brief Searches backwards for where and removes it and everything + /// after it + /// @param where Character to find and consume + /// @return A reference to the current string_view object + DAW_ATTRIB_INLINE constexpr basic_string_view & + remove_suffix_until( CharT where ) { + auto const pos = rfind( where ); + resize( pos == npos ? 0U : pos ); + return *this; + } + + /// @brief Searches backwards for where and removes everything after it + /// @param where Character to find and retain + /// @return A reference to the current string_view object + DAW_ATTRIB_INLINE constexpr basic_string_view & + remove_suffix_until( CharT where, nodiscard_t ) { + auto const pos = rfind( where ); + resize( pos == npos ? 0U : pos + 1U ); + return *this; + } + + /// @brief Searches backwards for where and removes it and everything + /// after it + /// @param where String to find and consume + /// @return A reference to the current string_view object + DAW_ATTRIB_INLINE constexpr basic_string_view & + remove_suffix_until( basic_string_view where ) { + auto const pos = rfind( where ); + resize( pos == npos ? 0U : pos ); + return *this; + } + + /// @brief Searches backwards for where and removes everything after it + /// @param where String to find and retain + /// @return A reference to the current string_view object + DAW_ATTRIB_INLINE constexpr basic_string_view & + remove_suffix_until( basic_string_view where, nodiscard_t ) { + auto const pos = rfind( where ); + resize( pos == npos ? 0U : pos + where.size( ) ); + return *this; + } + + /// @brief Removes the suffix starting at the last position where pred is + /// true + /// @tparam UnaryPredicate A predicate taking a single CharT + /// @param pred Predicate identifying where to stop + /// @return A reference to the current string_view object + template + DAW_REQ_UNARY_PRED_REQ( UnaryPredicate, CharT ) + DAW_ATTRIB_INLINE constexpr basic_string_view &remove_suffix_until( + UnaryPredicate pred ) { + auto const pos = find_last_of_if( pred ); + resize( pos == npos ? 0U : pos ); + return *this; + } + + /// @brief Removes the suffix after the last position where pred is true + /// @tparam UnaryPredicate A predicate taking a single CharT + /// @param pred Predicate identifying where to stop + /// @return A reference to the current string_view object + template + DAW_REQ_UNARY_PRED_REQ( UnaryPredicate, CharT ) + DAW_ATTRIB_INLINE + constexpr basic_string_view &remove_suffix_until( UnaryPredicate pred, + nodiscard_t ) { + auto const pos = find_last_of_if( pred ); + resize( pos == npos + ? 0U + : pos + sv2_details::find_predicate_result_size( pred ) ); + return *this; + } + /// @brief Set the size of the range /// @param new_size New size of range /// @pre size( ) >= new_size @@ -2532,13 +2645,12 @@ namespace daw { return find_first_not_of( basic_string_view( s, N - 1 ), 0 ); } - [[nodiscard]] constexpr bool - all_of( basic_string_view v, size_type pos = 0 ) const { + [[nodiscard]] constexpr bool all_of( basic_string_view v, + size_type pos = 0 ) const { return find_first_not_of( v, pos ) == npos; } - [[nodiscard]] constexpr bool all_of( CharT c, - size_type pos = 0 ) const { + [[nodiscard]] constexpr bool all_of( CharT c, size_type pos = 0 ) const { return find_first_not_of( c, pos ) == npos; } @@ -2858,6 +2970,13 @@ namespace daw { return *this; } + DAW_ATTRIB_INLINE constexpr basic_string_view & + remove_prefix_while( basic_string_view characters ) noexcept { + auto const pos = find_first_not_of( characters ); + remove_prefix( pos ); + return *this; + } + constexpr basic_string_view &trim_prefix( ) noexcept { remove_prefix_while( is_space{ } ); return *this; @@ -2886,6 +3005,13 @@ namespace daw { return *this; } + DAW_ATTRIB_INLINE constexpr basic_string_view & + remove_suffix_while( basic_string_view characters ) noexcept { + auto const pos = find_last_not_of( characters ); + resize( pos + 1U ); + return *this; + } + constexpr basic_string_view &trim_suffix( ) noexcept { remove_suffix_while( is_space{ } ); return *this; @@ -2909,6 +3035,20 @@ namespace daw { return result; } + constexpr basic_string_view & + trim( basic_string_view while_this ) noexcept { + (void)remove_prefix_while( while_this ); + (void)remove_suffix_while( while_this ); + return *this; + } + + [[nodiscard]] constexpr basic_string_view + trim_copy( basic_string_view while_this ) const noexcept { + auto result = *this; + result.trim( while_this ); + return result; + } + /// @brief Decrement the data( ) pointer by 1. /// @pre data( ) != null and data( ) - 1 is a valid object constexpr void expand_prefix( ) noexcept { diff --git a/tests/daw_string_view2_test.cpp b/tests/daw_string_view2_test.cpp index 82cb79833..514089104 100644 --- a/tests/daw_string_view2_test.cpp +++ b/tests/daw_string_view2_test.cpp @@ -1368,6 +1368,36 @@ namespace daw { daw_expecting( result, " "_sv ); } + void daw_pop_back_while_sv_test_001( ) { + daw::sv2::string_view sv = "This is a test "; + auto result = sv.pop_back_while( ' ' ); + daw_expecting( sv, "This is a test"_sv ); + daw_expecting( result, " "_sv ); + } + + void daw_pop_back_while_sv_test_002( ) { + daw::sv2::string_view sv = "This is a test \t\n"; + auto result = sv.pop_back_while( " \t\n" ); + daw_expecting( sv, "This is a test"_sv ); + daw_expecting( result, " \t\n"_sv ); + } + + void daw_pop_back_while_pred_test_001( ) { + daw::sv2::string_view sv = "test123"; + auto result = sv.pop_back_while( []( char c ) { + return c >= '0' and c <= '9'; + } ); + daw_expecting( sv, "test"_sv ); + daw_expecting( result, "123"_sv ); + + daw::sv2::string_view all = "123"; + daw_expecting( all.pop_back_while( []( char c ) { + return c >= '0' and c <= '9'; + } ), + "123"_sv ); + daw_expecting( all.empty( ), true ); + } + void daw_pop_front_test_001( ) { daw::sv2::string_view sv = "This is a test"; auto result = sv.pop_front( ); @@ -1716,6 +1746,50 @@ namespace daw { daw_expecting( sz, 5 ); } + void daw_remove_suffix_until_test_001( ) { + daw::sv2::string_view sv = "0123456789"; + sv.remove_suffix_until( '5' ); + daw_expecting( sv, "01234"_sv ); + } + + void daw_remove_suffix_until_test_002( ) { + daw::sv2::string_view sv = "0123456789"; + sv.remove_suffix_until( '5', daw::nodiscard ); + daw_expecting( sv, "012345"_sv ); + } + + void daw_remove_suffix_until_test_003( ) { + daw::sv2::string_view sv = "left::middle::right"; + sv.remove_suffix_until( "::" ); + daw_expecting( sv, "left::middle"_sv ); + + daw::sv2::string_view retained = "left::middle::right"; + retained.remove_suffix_until( "::", daw::nodiscard ); + daw_expecting( retained, "left::middle::"_sv ); + } + + void daw_remove_suffix_until_test_004( ) { + daw::sv2::string_view sv = "abc1def2ghi"; + sv.remove_suffix_until( []( char c ) { + return c >= '0' and c <= '9'; + } ); + daw_expecting( sv, "abc1def"_sv ); + + daw::sv2::string_view retained = "abc1def2ghi"; + retained.remove_suffix_until( + []( char c ) { + return c >= '0' and c <= '9'; + }, + daw::nodiscard ); + daw_expecting( retained, "abc1def2"_sv ); + } + + void daw_remove_suffix_until_test_005( ) { + daw::sv2::string_view sv = "test"; + sv.remove_suffix_until( 'x' ); + daw_expecting( sv.empty( ), true ); + } + struct FooSV { using iterator = char const *; using size_type = std::size_t; @@ -1835,6 +1909,47 @@ namespace daw { daw_expecting( sv2, "Hello" ); } + void daw_trim_characters( ) { + daw::sv2::string_view sv = " \t,;Hello, world;\n "; + auto const *const original_data = sv.data( ); + auto &result = sv.trim( " \t\n,;" ); + daw_expecting( std::addressof( result ), std::addressof( sv ) ); + daw_expecting( sv, "Hello, world" ); + daw_expecting( sv.data( ), original_data + 4 ); + } + + void daw_trim_characters_copy( ) { + daw::sv2::string_view sv = "...Hello!?"; + auto const result = sv.trim_copy( ".!?" ); + daw_expecting( result, "Hello" ); + daw_expecting( sv, "...Hello!?" ); + } + + void daw_trim_characters_edge_cases( ) { + daw::sv2::string_view no_matches = "Hello"; + no_matches.trim( ".!?" ); + daw_expecting( no_matches, "Hello" ); + + daw::sv2::string_view all_matches = ".!?..!"; + all_matches.trim( ".!?" ); + daw_expecting( all_matches.empty( ), true ); + + daw::sv2::string_view empty_set = "Hello"; + empty_set.trim( "" ); + daw_expecting( empty_set, "Hello" ); + + daw::sv2::string_view empty = ""; + empty.trim( ".!?" ); + daw_expecting( empty.empty( ), true ); + } + + constexpr bool daw_trim_characters_constexpr( ) { + daw::sv2::string_view sv = "--value++"; + return sv.trim_copy( "+-" ) == "value" and + sv.trim( "+-" ) == "value"; + } + static_assert( daw_trim_characters_constexpr( ) ); + namespace daw_array_ctad_impl { template constexpr auto foo( S &&s ) { @@ -2172,6 +2287,9 @@ int main( ) daw::daw_pop_front_until_sv_test_001( ); daw::daw_pop_front_while_sv_test_001( ); daw::daw_pop_front_while_sv_test_002( ); + daw::daw_pop_back_while_sv_test_001( ); + daw::daw_pop_back_while_sv_test_002( ); + daw::daw_pop_back_while_pred_test_001( ); daw::daw_pop_back_count_test_001( ); daw::daw_pop_back_until_sv_test_001( ); daw::daw_pop_back_until_sv_test_002( ); @@ -2201,6 +2319,11 @@ int main( ) daw::daw_remove_prefix_until_test_004( ); daw::daw_remove_suffix_while_test_001( ); daw::daw_remove_suffix_while_test_002( ); + daw::daw_remove_suffix_until_test_001( ); + daw::daw_remove_suffix_until_test_002( ); + daw::daw_remove_suffix_until_test_003( ); + daw::daw_remove_suffix_until_test_004( ); + daw::daw_remove_suffix_until_test_005( ); daw::daw_arbutrary_string_view_list_001( ); daw::daw_trim_prefix_test_001( ); daw::daw_trim_prefix_test_002( ); @@ -2220,6 +2343,9 @@ int main( ) #endif daw::daw_trim( ); daw::daw_trim_copy( ); + daw::daw_trim_characters( ); + daw::daw_trim_characters_copy( ); + daw::daw_trim_characters_edge_cases( ); daw::daw_array_ctad( ); daw::daw_find_first_match( ); #if defined( DAW_HAS_CPP20_3WAY_COMPARE )