openMSX
StringOp.hh
Go to the documentation of this file.
1#ifndef STRINGOP_HH
2#define STRINGOP_HH
3
4#include "IterableBitSet.hh"
5#include "stringsp.hh"
6#include <algorithm>
7#include <charconv>
8#include <concepts>
9#include <cstdint>
10#include <iomanip>
11#include <limits>
12#include <optional>
13#include <sstream>
14#include <string>
15#include <string_view>
16#include <type_traits>
17#include <utility>
18#if defined(__APPLE__)
19#include <CoreFoundation/CoreFoundation.h>
20#endif
21
22namespace StringOp
23{
43 template<std::integral T> [[nodiscard]] std::optional<T> stringTo(std::string_view s);
44
48 template<int BASE, std::integral T> [[nodiscard]] std::optional<T> stringToBase(std::string_view s);
49
50 [[nodiscard]] bool stringToBool(std::string_view str);
51
52 [[nodiscard]] std::string toLower(std::string_view str);
53
54 void trimRight(std::string& str, const char* chars);
55 void trimRight(std::string& str, char chars);
56 void trimRight(std::string_view& str, std::string_view chars);
57 void trimRight(std::string_view& str, char chars);
58 void trimLeft (std::string& str, const char* chars);
59 void trimLeft (std::string& str, char chars);
60 void trimLeft (std::string_view& str, std::string_view chars);
61 void trimLeft (std::string_view& str, char chars);
62 void trim (std::string_view& str, std::string_view chars);
63 void trim (std::string_view& str, char chars);
64
65 [[nodiscard]] std::pair<std::string_view, std::string_view> splitOnFirst(
66 std::string_view str, std::string_view chars);
67 [[nodiscard]] std::pair<std::string_view, std::string_view> splitOnFirst(
68 std::string_view str, char chars);
69 [[nodiscard]] std::pair<std::string_view, std::string_view> splitOnLast(
70 std::string_view str, std::string_view chars);
71 [[nodiscard]] std::pair<std::string_view, std::string_view> splitOnLast(
72 std::string_view str, char chars);
73 [[nodiscard]] IterableBitSet<64> parseRange(std::string_view str,
74 unsigned min, unsigned max);
75
76 //[[nodiscard]] std::vector<std::string_view> split(std::string_view str, char chars);
77
79 KEEP_EMPTY_PARTS, // "a,b,,c" -> "a", "b", "", "c"
80 REMOVE_EMPTY_PARTS // "a,b,,c" -> "a", "b", "c" BUT ",,a,b" -> "", "a", "b" (keeps one empty part in front)
81 };
82 template<KeepOrRemoveEmptyParts keepOrRemove = KEEP_EMPTY_PARTS, typename Separators>
83 [[nodiscard]] inline auto split_view(std::string_view str, Separators separators) {
84 struct Sentinel {};
85
86 struct Iterator {
87 using value_type = std::string_view;
88 using difference_type = ptrdiff_t;
89
90 Iterator() = default;
91 Iterator(const Iterator&) = default;
92 Iterator& operator=(const Iterator&) = default;
93
94 Iterator(std::string_view str_, Separators separators_)
95 : str(str_), separators(separators_) {
96 str.remove_prefix(skipSeparators(0));
97 next_p();
98 }
99
100 [[nodiscard]] value_type operator*() const {
101 difference_type dummy; (void)dummy; // avoid warning about unused typedef
102 return {str.data(), p};
103 }
104
105 Iterator& operator++() {
106 if (p < str.size()) {
107 str.remove_prefix(skipSeparators(p + 1));
108 next_p();
109 } else {
110 str = "";
111 }
112 return *this;
113 }
114 Iterator operator++(int) { auto copy = *this; ++(*this); return copy; }
115
116 [[nodiscard]] bool operator==(const Iterator&) const = default;
117 [[nodiscard]] bool operator==(Sentinel) const { return str.empty(); }
118
119 private:
120 static bool isSeparator(char c, char separators) {
121 return c == separators;
122 }
123 static bool isSeparator(char c, std::string_view separators) {
124 return separators.find(c) != std::string_view::npos;
125 }
126
127 void next_p() {
128 p = 0;
129 while ((p < str.size()) && !isSeparator(str[p], separators)) ++p;
130 }
131
132 std::string_view::size_type skipSeparators(std::string_view::size_type pos) const {
133 if (keepOrRemove == REMOVE_EMPTY_PARTS) {
134 while ((pos < str.size()) && isSeparator(str[pos], separators)) ++pos;
135 }
136 return pos;
137 }
138
139 private:
140 std::string_view str;
141 std::string_view::size_type p;
142 Separators separators;
143 };
144 static_assert(std::forward_iterator<Iterator>);
145 static_assert(std::sentinel_for<Sentinel, Iterator>);
146
147 struct Splitter {
148 std::string_view str;
149 Separators separators;
150
151 [[nodiscard]] auto begin() const { return Iterator{str, separators}; }
152 [[nodiscard]] auto end() const { return Sentinel{}; }
153 };
154
155 return Splitter{str, separators};
156 }
157
158 // case insensitive less-than operator
159 struct caseless {
160 [[nodiscard]] bool operator()(std::string_view s1, std::string_view s2) const {
161 auto m = std::min(s1.size(), s2.size());
162 int r = strncasecmp(s1.data(), s2.data(), m);
163 return (r != 0) ? (r < 0) : (s1.size() < s2.size());
164 }
165 };
166 // case insensitive greater-than operator
168 [[nodiscard]] bool operator()(std::string_view s1, std::string_view s2) const {
169 auto m = std::min(s1.size(), s2.size());
170 int r = strncasecmp(s1.data(), s2.data(), m);
171 return (r != 0) ? (r > 0) : (s1.size() > s2.size());
172 }
173 };
174 struct casecmp {
175 [[nodiscard]] bool operator()(std::string_view s1, std::string_view s2) const {
176 if (s1.size() != s2.size()) return false;
177 return strncasecmp(s1.data(), s2.data(), s1.size()) == 0;
178 }
179 };
180
181 [[nodiscard]] inline bool containsCaseInsensitive(std::string_view haystack, std::string_view needle)
182 {
183 return std::search(haystack.begin(), haystack.end(),
184 needle.begin(), needle.end(),
185 [](char x, char y) { return toupper(x) == toupper(y); })
186 != haystack.end();
187 }
188
189#if defined(__APPLE__)
190 [[nodiscard]] std::string fromCFString(CFStringRef str);
191#endif
192
193 template<int BASE, std::integral T>
194 [[nodiscard]] std::optional<T> stringToBase(std::string_view s)
195 {
196 T result = {}; // dummy init to avoid warning
197 auto b = s.data();
198 auto e = s.data() + s.size();
199 if (auto [p, ec] = std::from_chars(b, e, result, BASE);
200 (ec == std::errc()) && (p == e)) {
201 return result;
202 }
203 return std::nullopt;
204 }
205
206 template<std::integral T>
207 [[nodiscard]] std::optional<T> stringTo(std::string_view s)
208 {
209 if (s.empty()) [[unlikely]] return {};
210 if constexpr (std::is_signed_v<T>) {
211 bool negate = false;
212 if (s[0] == '-') [[unlikely]] {
213 negate = true;
214 s.remove_prefix(1);
215 }
216
217 using U = std::make_unsigned_t<T>;
218 auto tmp = stringTo<U>(s);
219 if (!tmp) [[unlikely]] return {};
220
221 U max = U(std::numeric_limits<T>::max()) + 1; // 0x8000
222 if (negate) [[unlikely]] {
223 if (*tmp > max) [[unlikely]] return {}; // 0x8000
224 return T(~*tmp + 1);
225 } else {
226 if (*tmp >= max) [[unlikely]] return {}; // 0x7fff
227 return T(*tmp);
228 }
229 } else {
230 if (s[0] != '0') [[likely]] {
231 return stringToBase<10, T>(s);
232 } else {
233 if (s.size() == 1) return T(0);
234 if ((s[1] == 'x') || (s[1] == 'X')) [[likely]] {
235 s.remove_prefix(2);
236 return stringToBase<16, T>(s);
237 } else if ((s[1] == 'b') || (s[1] == 'B')) {
238 s.remove_prefix(2);
239 return stringToBase<2, T>(s);
240 } else {
241 return stringToBase<10, T>(s);
242 }
243 }
244 }
245 }
246}
247
248#endif
IterableBitSet.
void trim(string_view &str, string_view chars)
Definition StringOp.cc:79
IterableBitSet< 64 > parseRange(string_view str, unsigned min, unsigned max)
Definition StringOp.cc:177
bool stringToBool(string_view str)
Definition StringOp.cc:12
std::optional< T > stringToBase(std::string_view s)
As above, but without dynamic base detection.
Definition StringOp.hh:194
std::pair< string_view, string_view > splitOnLast(string_view str, string_view chars)
Definition StringOp.cc:108
void trimRight(string &str, const char *chars)
Definition StringOp.cc:29
std::optional< T > stringTo(std::string_view s)
Convert a string to an integral type 'T' (int, uint64_t, ...).
Definition StringOp.hh:207
std::pair< string_view, string_view > splitOnFirst(string_view str, string_view chars)
Definition StringOp.cc:91
void trimLeft(string &str, const char *chars)
Definition StringOp.cc:58
std::string toLower(std::string_view str)
Definition StringOp.cc:22
bool containsCaseInsensitive(std::string_view haystack, std::string_view needle)
Definition StringOp.hh:181
auto split_view(std::string_view str, Separators separators)
Definition StringOp.hh:83
KeepOrRemoveEmptyParts
Definition StringOp.hh:78
@ KEEP_EMPTY_PARTS
Definition StringOp.hh:79
@ REMOVE_EMPTY_PARTS
Definition StringOp.hh:80
bool operator()(std::string_view s1, std::string_view s2) const
Definition StringOp.hh:175
bool operator()(std::string_view s1, std::string_view s2) const
Definition StringOp.hh:160
bool operator()(std::string_view s1, std::string_view s2) const
Definition StringOp.hh:168
constexpr uint128 operator*(const uint128 &a, const uint128 &b)
Definition uint128.hh:191
constexpr bool operator==(const zstring_view &x, const zstring_view &y)
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)