neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
string_utils.hpp
Go to the documentation of this file.
1// string_utils.hpp
2/*
3 * Copyright (c) 2007 Leigh Johnston.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * * Neither the name of Leigh Johnston nor the names of any
19 * other contributors to this software may be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*/
35
36#pragma once
37
38#include <neolib/neolib.hpp>
39#include <string>
40#include <vector>
41#include <list>
42#include <set>
43#include <map>
44#include <variant>
45#include <locale>
46#include <format>
50
51namespace neolib
52{
53 struct NEOLIB_EXPORT comma_as_whitespace : std::ctype<char>
54 {
55 static const mask* make_table()
56 {
57 static std::vector<mask> v(classic_table(), classic_table() + table_size);
58 v[','] |= space;
59 return &v[0];
60 }
61 comma_as_whitespace(std::size_t refs = 0) : ctype{ make_table(), false, refs } {}
62 };
63
64 struct NEOLIB_EXPORT comma_and_brackets_as_whitespace : std::ctype<char>
65 {
66 static const mask* make_table()
67 {
68 static std::vector<mask> v(classic_table(), classic_table() + table_size);
69 v[','] |= space;
70 v['['] |= space;
71 v[']'] |= space;
72 v['('] |= space;
73 v[')'] |= space;
74 v['{'] |= space;
75 v['}'] |= space;
76 return &v[0];
77 }
78 comma_and_brackets_as_whitespace(std::size_t refs = 0) : ctype{ make_table(), false, refs } {}
79 };
80
81 struct NEOLIB_EXPORT comma_only_whitespace : std::ctype<char>
82 {
83 static const mask* make_table()
84 {
85 static std::vector<mask> v(classic_table(), classic_table() + table_size);
86 v[','] |= space;
87 v[' '] = alpha;
88 return &v[0];
89 }
90 comma_only_whitespace(std::size_t refs = 0) : ctype{ make_table(), false, refs } {}
91 };
92
93 template <typename T>
94 inline std::string to_std_string(T const& aValue)
95 {
96 std::ostringstream oss;
97 oss << aValue;
98 return oss.str();
99 }
100
101 template <typename T>
102 inline T from_std_string(std::string const& aValueAsString)
103 {
104 T result;
105 std::istringstream iss{ aValueAsString };
106 iss >> result;
107 return result;
108
109 }
110
111 template <typename T>
112 inline string to_string(T const& aValue)
113 {
114 return to_std_string(aValue);
115 }
116
117 template <typename T>
118 inline T from_string(i_string const& aValueAsString)
119 {
120 return from_std_string<T>(aValueAsString.to_std_string());
121 }
122
123 template <typename FwdIter1, typename FwdIter2, typename ResultContainer>
124 inline FwdIter1 tokens(FwdIter1 aFirst, FwdIter1 aLast, FwdIter2 aDelimeterFirst, FwdIter2 aDelimiterLast, ResultContainer& aTokens, std::size_t aMaxTokens = 0, bool aSkipEmptyTokens = true, bool aDelimeterIsSubsequence = false)
125 {
126 if (aFirst == aLast)
127 return aFirst;
128 typedef typename ResultContainer::value_type value_type;
129 if (aDelimeterFirst == aDelimiterLast)
130 {
131 aTokens.push_back(value_type{ aFirst, aLast });
132 return aLast;
133 }
134 FwdIter1 b = aFirst;
135 FwdIter1 e = aDelimeterIsSubsequence ? std::search(b, aLast, aDelimeterFirst, aDelimiterLast) : std::find_first_of(b, aLast, aDelimeterFirst, aDelimiterLast);
136 std::size_t tokens = 0;
137 std::optional<FwdIter1> last;
138 while(e != aLast && (aMaxTokens == 0 || tokens < aMaxTokens))
139 {
140 if (b == e && !aSkipEmptyTokens)
141 {
142 aTokens.push_back(value_type{ b, b });
143 last = b;
144 ++tokens;
145 }
146 else if (b != e)
147 {
148 aTokens.push_back(value_type{ b, e });
149 last = e;
150 ++tokens;
151 }
152 b = e;
153 std::advance(b, aDelimeterIsSubsequence ? std::distance(aDelimeterFirst, aDelimiterLast) : 1);
154 e = aDelimeterIsSubsequence ? std::search(b, aLast, aDelimeterFirst, aDelimiterLast) : std::find_first_of(b, aLast, aDelimeterFirst, aDelimiterLast);
155 }
156 if (b != e && (aMaxTokens == 0 || tokens < aMaxTokens))
157 {
158 aTokens.push_back(value_type{ b, e });
159 b = e;
160 }
161 else if (b == e && last && last.value() != e && !aSkipEmptyTokens)
162 aTokens.push_back(value_type{ e, e });
163 return b;
164 }
165
166 template <typename FwdIter, typename CharT, typename Traits, typename Alloc, typename ResultContainer>
167 inline void tokens(FwdIter aFirst, FwdIter aLast, const std::basic_string<CharT, Traits, Alloc>& aDelimeter, ResultContainer& aTokens, std::size_t aMaxTokens = 0, bool aSkipEmptyTokens = true, bool aDelimeterIsSubsequence = false)
168 {
169 tokens(aFirst, aLast, aDelimeter.begin(), aDelimeter.end(), aTokens, aMaxTokens, aSkipEmptyTokens, aDelimeterIsSubsequence);
170 }
171
172 template <typename CharT, typename Traits, typename Alloc, typename ResultContainer>
173 inline void tokens(const std::basic_string<CharT, Traits, Alloc>& aLine, const std::basic_string<CharT, Traits, Alloc>& aDelimeter, ResultContainer& aTokens, std::size_t aMaxTokens = 0, bool aSkipEmptyTokens = true, bool aDelimeterIsSubsequence = false)
174 {
175 tokens(aLine.begin(), aLine.end(), aDelimeter.begin(), aDelimeter.end(), aTokens, aMaxTokens, aSkipEmptyTokens, aDelimeterIsSubsequence);
176 }
177
178 template <typename FwdIter, typename CharT, typename Traits, typename Alloc>
179 inline std::vector<std::basic_string<CharT, Traits, Alloc>> tokens(FwdIter aFirst, FwdIter aLast, const std::basic_string<CharT, Traits, Alloc>& aDelimeter, std::size_t aMaxTokens = 0, bool aSkipEmptyTokens = true, bool aDelimeterIsSubsequence = false)
180 {
181 std::vector<std::basic_string<CharT, Traits, Alloc>> results;
182 tokens(aFirst, aLast, aDelimeter.begin(), aDelimeter.end(), results, aMaxTokens, aSkipEmptyTokens, aDelimeterIsSubsequence);
183 return results;
184 }
185
186 template <typename CharT, typename Traits, typename Alloc>
187 inline std::vector<std::basic_string<CharT, Traits, Alloc>> tokens(const std::basic_string<CharT, Traits, Alloc>& aLine, const std::basic_string<CharT, Traits, Alloc>& aDelimeter, std::size_t aMaxTokens = 0, bool aSkipEmptyTokens = true, bool aDelimeterIsSubsequence = false)
188 {
189 std::vector<std::basic_string<CharT, Traits, Alloc>> results;
190 tokens(aLine.begin(), aLine.end(), aDelimeter.begin(), aDelimeter.end(), results, aMaxTokens, aSkipEmptyTokens, aDelimeterIsSubsequence);
191 return results;
192 }
193
194 inline std::string to_string(const std::pair<char const*, char const*>& aIterPair)
195 {
196 return std::string(aIterPair.first, aIterPair.second);
197 }
198
199 template <typename CharT, typename Traits, typename Alloc>
200 inline std::basic_string<CharT, Traits, Alloc> to_lower(const std::basic_string<CharT, Traits, Alloc>& aString)
201 {
202 static boost::locale::generator gen;
203 static std::locale loc = gen("en_US.UTF-8");
204 return boost::locale::to_lower(aString, loc);
205 }
206
207 template <typename CharT>
208 inline CharT to_lower(CharT aCharacter)
209 {
210 return to_lower(std::basic_string<CharT>(1, aCharacter))[0];
211 }
212
213 template <typename CharT, typename Traits, typename Alloc>
214 inline std::basic_string<CharT, Traits, Alloc> to_upper(const std::basic_string<CharT, Traits, Alloc>& aString)
215 {
216 static boost::locale::generator gen;
217 static std::locale loc = gen("en_US.UTF-8");
218 return boost::locale::to_upper(aString, loc);
219 }
220
221 template <typename CharT>
222 inline CharT to_upper(CharT aCharacter)
223 {
224 return to_upper(std::basic_string<CharT>(1, aCharacter))[0];
225 }
226
227 struct NEOLIB_EXPORT string_span : std::pair<std::size_t, std::size_t>
228 {
229 typedef std::pair<std::size_t, std::size_t> span;
230 typedef unsigned int type;
231 string_span(const span& aSpan, type aType = 0) : span(aSpan), iType(aType) {}
232 string_span(std::size_t aFirst, std::size_t aSecond, type aType = 0) : span(aFirst, aSecond), iType(aType) {}
233 string_span& operator=(const span& aSpan) { span::operator=(aSpan); return *this; }
235 };
236 typedef std::vector<string_span> string_spans;
237
238 template <typename CharT, typename Traits, typename Alloc>
239 inline bool replace_string(std::basic_string<CharT, Traits, Alloc>& aString, const std::basic_string<CharT, Traits, Alloc>& aSearch, const std::basic_string<CharT, Traits, Alloc>& aReplace, string_spans* aSpans, const string_span::type* aNewSpanType)
240 {
241 if (aString.empty())
242 return false;
243 typedef std::basic_string<CharT, Traits, Alloc> string;
244 typename string::size_type pos = 0;
245 bool replaced = false;
246 while ((pos = aString.find(aSearch, pos)) != string::npos)
247 {
248 aString.replace(pos, aSearch.size(), aReplace);
249 if (aSpans != 0)
250 {
251 if (aNewSpanType && aSpans->empty())
252 aSpans->push_back(string_span(pos, pos + aReplace.size(), *aNewSpanType));
253 else
254 {
255 for (string_spans::iterator i = aSpans->begin(); i != aSpans->end(); ++i)
256 {
257 if (i->first != i->second)
258 {
259 if (i->first >= pos)
260 {
261 if (aSearch.size() > aReplace.size())
262 i->first -= aSearch.size() - aReplace.size();
263 else
264 i->first += aReplace.size() - aSearch.size();
265 }
266 if (i->second >= pos)
267 {
268 if (aSearch.size() > aReplace.size())
269 i->second -= aSearch.size() - aReplace.size();
270 else
271 i->second += aReplace.size() - aSearch.size();
272 }
273 }
274 }
275 }
276 }
277 pos += aReplace.size();
278 replaced = true;
279 }
280 return replaced;
281 }
282
283 template <typename CharT, typename Traits, typename Alloc>
284 inline bool replace_string(std::basic_string<CharT, Traits, Alloc>& aString, const std::basic_string<CharT, Traits, Alloc>& aSearch, const std::basic_string<CharT, Traits, Alloc>& aReplace)
285 {
286 return replace_string(aString, aSearch, aReplace, 0, static_cast<string_span::type*>(0));
287 }
288
289 template <typename CharT, typename Traits, typename Alloc>
290 inline bool replace_string(std::basic_string<CharT, Traits, Alloc>& aString, const std::basic_string<CharT, Traits, Alloc>& aSearch, const std::basic_string<CharT, Traits, Alloc>& aReplace, string_spans* aSpans)
291 {
292 return replace_string(aString, aSearch, aReplace, aSpans, static_cast<string_span::type*>(0));
293 }
294
295 template <typename CharT, typename Traits, typename Alloc>
296 inline bool replace_string(std::basic_string<CharT, Traits, Alloc>& aString, const std::basic_string<CharT, Traits, Alloc>& aSearch, const std::basic_string<CharT, Traits, Alloc>& aReplace, string_spans* aSpans, string_span::type aNewSpanType)
297 {
298 return replace_string(aString, aSearch, aReplace, aSpans, &aNewSpanType);
299 }
300
301 template <typename CharT, typename Traits, typename Alloc>
302 inline std::string& remove_leading(std::basic_string<CharT, Traits, Alloc>& aString, const std::basic_string<CharT, Traits, Alloc>& aLeading)
303 {
304 typename std::basic_string<CharT, Traits, Alloc>::size_type pos = aString.find_first_not_of(aLeading);
305 if (pos != std::basic_string<CharT, Traits, Alloc>::npos)
306 aString.erase(aString.begin(), aString.begin() + pos);
307 else
308 aString.clear();
309 return aString;
310 }
311
312 template <typename CharT, typename Traits, typename Alloc>
313 inline std::string& remove_trailing(std::basic_string<CharT, Traits, Alloc>& aString, const std::basic_string<CharT, Traits, Alloc>& aTrailing)
314 {
315 typename std::basic_string<CharT, Traits, Alloc>::size_type pos = aString.find_last_not_of(aTrailing);
316 if (pos != std::basic_string<CharT, Traits, Alloc>::npos)
317 aString.erase(aString.begin() + pos + 1, aString.end());
318 else
319 aString.clear();
320 return aString;
321 }
322
323 template <typename CharT, typename Traits, typename Alloc>
324 inline std::string& remove_leading_and_trailing(std::basic_string<CharT, Traits, Alloc>& aString, const std::basic_string<CharT, Traits, Alloc>& aLeadingTrailing)
325 {
326 remove_leading(aString, aLeadingTrailing);
327 remove_trailing(aString, aLeadingTrailing);
328 return aString;
329 }
330
331 template <typename CharT>
332 inline bool contains_character(const CharT* aSequence, CharT aCharacter)
333 {
334 return strchr(aSequence, aCharacter) != NULL;
335 }
336
337 template <typename CharT, typename Traits, typename Alloc>
338 inline typename std::basic_string<CharT, Traits, Alloc>::size_type reverse_find_last_of(const std::basic_string<CharT, Traits, Alloc>& aString, const std::basic_string<CharT, Traits, Alloc>& aSequence, typename std::basic_string<CharT, Traits, Alloc>::size_type aPosition)
339 {
340 if (aString.empty())
341 return std::basic_string<CharT, Traits, Alloc>::npos;
342 typename std::basic_string<CharT, Traits, Alloc>::size_type last = std::basic_string<CharT, Traits, Alloc>::npos;
343 for (;;)
344 {
345 if (contains_character(aSequence.c_str(), aString[aPosition]))
346 {
347 last = aPosition;
348 if (aPosition == 0)
349 break;
350 --aPosition;
351 }
352 else
353 break;
354 }
355 return last;
356 }
357
358 template <typename CharT, typename Traits, typename Alloc>
359 inline typename std::basic_string<CharT, Traits, Alloc>::size_type reverse_find_first_of(const std::basic_string<CharT, Traits, Alloc>& aString, const std::basic_string<CharT, Traits, Alloc>& aSequence, typename std::basic_string<CharT, Traits, Alloc>::size_type aPosition)
360 {
361 if (aString.empty())
362 return std::basic_string<CharT, Traits, Alloc>::npos;
363 if (aPosition == std::basic_string<CharT, Traits, Alloc>::npos)
364 aPosition = aString.size() - 1;
365 for (;;)
366 {
367 if (contains_character(aSequence.c_str(), aString[aPosition]))
368 return aPosition;
369 else if (aPosition == 0)
370 return std::basic_string<CharT, Traits, Alloc>::npos;
371 --aPosition;
372 }
373 return std::basic_string<CharT, Traits, Alloc>::npos;
374 }
375
376 inline std::string parse_escapes(const std::string& aString)
377 {
378 std::string ret = aString;
379 std::string::size_type escapePos;
380 while((escapePos = ret.find("\\r")) != std::string::npos)
381 ret.replace(escapePos, 2, "\r");
382 while((escapePos = ret.find("\\n")) != std::string::npos)
383 ret.replace(escapePos, 2, "\n");
384 while((escapePos = ret.find("\\t")) != std::string::npos)
385 ret.replace(escapePos, 2, "\t");
386 return ret;
387 }
388
389 inline std::string parse_url_escapes(const std::string& aString)
390 {
391 std::string ret = aString;
392 for (std::string::size_type pos = 0; pos != ret.length(); ++pos)
393 if (ret[pos] == '%' && pos + 2 < ret.length())
394 ret.replace(pos, 3, 1, static_cast<char>(string_to_int32(ret.substr(pos + 1, 2), 16)));
395 return ret;
396 }
397
398 namespace detail
399 {
400 template<typename CharT>
401 inline CharT wildcard_match_any_string() { return '*'; }
402 template<>
403 inline char16_t wildcard_match_any_string<char16_t>() { return L'*'; }
404 template<typename CharT>
405 inline CharT wildcard_match_any_character() { return '?'; }
406 template<>
407 inline char16_t wildcard_match_any_character<char16_t>() { return L'?'; }
408
409 template <typename Traits>
411 {
412 typedef typename Traits::char_type char_type;
413 bool operator()(char_type c1, char_type c2) const
414 {
415 if (c2 == wildcard_match_any_character<char_type>())
416 return true;
417 else
418 return Traits::eq(c1, c2);
419 }
420 };
421 }
422
423 template <typename CharT, typename Traits, typename FwdIter>
424 inline bool do_wildcard_match(FwdIter aTextBegin, FwdIter aTextEnd, FwdIter aPatternBegin, FwdIter aPatternEnd)
425 {
426 typedef std::pair<FwdIter, FwdIter> substring_t;
427 typedef std::vector<substring_t> substrings_t;
428 substrings_t substrings;
429 CharT any_string = detail::wildcard_match_any_string<CharT>();
430 neolib::tokens(aPatternBegin, aPatternEnd, &any_string, &any_string+1, substrings);
431
432 FwdIter previousMatch = aTextBegin;
433 for (typename substrings_t::const_iterator i = substrings.begin(); i != substrings.end();)
434 {
435 substring_t theSubstring = *i++;
436 FwdIter nextMatch = std::search(previousMatch, aTextEnd, theSubstring.first, theSubstring.second, detail::wildcard_compare<Traits>());
437 if (nextMatch == aTextEnd)
438 return false;
439 if (theSubstring.first == aPatternBegin && nextMatch != aTextBegin)
440 return false;
441 if (theSubstring.second == aPatternEnd && !std::equal(aTextEnd - (theSubstring.second - theSubstring.first), aTextEnd, theSubstring.first, detail::wildcard_compare<Traits>()))
442 return false;
443 if (theSubstring.second == aPatternEnd && aTextEnd - nextMatch != theSubstring.second - theSubstring.first)
444 return false;
445 previousMatch = nextMatch + (theSubstring.second - theSubstring.first);
446 }
447 return true;
448 }
449
450 template <typename CharT, typename FwdIter>
451 inline bool wildcard_match(FwdIter aTextBegin, FwdIter aTextEnd, FwdIter aPatternBegin, FwdIter aPatternEnd)
452 {
453 return do_wildcard_match<CharT, std::char_traits<CharT>, FwdIter>(aTextBegin, aTextEnd, aPatternBegin, aPatternEnd);
454 }
455
456 template <typename CharT, typename Traits, typename Alloc>
457 inline bool wildcard_match(const std::basic_string<CharT, Traits, Alloc>& aText, const std::basic_string<CharT, Traits, Alloc>& aPattern)
458 {
459 return do_wildcard_match<CharT, Traits, std::basic_string<CharT, Traits, Alloc>::const_iterator>(aText.begin(), aText.end(), aPattern.begin(), aPattern.end());
460 }
461
463 {
464 private:
465 typedef std::function<void(char const*, char const*)> action_t;
466 struct state
467 {
468 std::map<char, state> match;
469 std::list<action_t> actions;
470 };
471 typedef std::tuple<const action_t*, char const*, char const*> result_t;
472 typedef std::set<result_t> results_t;
473 public:
475 public:
476 void add_pattern(std::string const& aPattern, action_t aAction);
477 void search(std::string const& aText, bool aRemoveSubmatches = true) const;
478 private:
479 void search(state const& aState, char const* aStart, char const* aNext, char const* aEnd, bool aSearchingWildcard, results_t& aResults) const;
480 void rebuild();
481 private:
482 std::map<std::string, action_t> iPatterns;
483 state iRoot;
484 };
485
487 {
489 {
490 std::size_t arg;
491 std::ptrdiff_t begin;
492 std::ptrdiff_t end;
493
494 constexpr std::strong_ordering operator<=>(formatted_arg const&) const noexcept = default;
495 };
496 typedef std::vector<formatted_arg> arg_list;
497
498 std::string text;
500
501 arg_list::const_iterator find_arg(std::size_t aArgIndex) const
502 {
503 return std::find_if(args.begin(), args.end(), [&](auto const& a) { return a.arg == aArgIndex; });
504 }
505 bool has_arg(std::size_t aArgIndex) const
506 {
507 return find_arg(aArgIndex) != args.end();
508 }
509 std::string_view arg_span(std::size_t aArgIndex) const
510 {
511 auto existing = find_arg(aArgIndex);
512 if (existing != args.end())
513 return { std::next(text.begin(), existing->begin), std::next(text.begin(), existing->end) };
514 throw std::format_error("neolib::format_result");
515 }
516 arg_list::const_iterator arg_spanning(std::ptrdiff_t aPos) const
517 {
518 return std::find_if(args.begin(), args.end(), [&](auto const& a) { return aPos >= a.begin && aPos < a.end; });
519 }
520 arg_list::const_iterator arg_after(std::ptrdiff_t aPos) const
521 {
522 auto existing = arg_spanning(aPos);
523 if (existing == args.end())
524 {
525 existing = std::find_if(args.rbegin(), args.rend(), [&](auto const& a) { return aPos >= a.end; }).base();
526 if (existing != args.begin())
527 --existing;
528 }
529 if (existing != args.end())
530 ++existing;
531 return existing;
532 }
533 };
534
535 template <typename... Args>
536 inline format_result format(std::string_view const& aFormat, Args&&... aArgs)
537 {
538 format_result result;
539 auto next = aFormat.begin();
540 while (next != aFormat.end())
541 {
542 auto nextArg = std::find(next, aFormat.end(), '{');
543 if (nextArg == aFormat.end())
544 {
545 result.text.append(next, nextArg);
546 next = nextArg;
547 }
548 else if (std::next(nextArg) == aFormat.end())
549 throw std::format_error("neolib::format");
550 else if (*std::next(nextArg) == '{')
551 {
552 result.text.append(next, std::next(nextArg, 2));
553 next = std::next(nextArg, 2);
554 }
555 else
556 {
557 result.text.append(next, nextArg);
558 auto nextArgEnd = std::find(nextArg, aFormat.end(), '}');
559 if (nextArgEnd == aFormat.end())
560 throw std::format_error("neolib::format");
561 else
562 {
563 // todo: add support for omission of arg-id in format string
564 ++nextArgEnd;
565 next = nextArgEnd;
566 auto argIdTerminators = { ':', '}' };
567 auto argIdEnd = std::find_first_of(nextArg, nextArgEnd, argIdTerminators.begin(), argIdTerminators.end());
568 std::optional<std::size_t> argId;
569 if (argIdEnd - nextArg >= 2)
570 argId = static_cast<std::size_t>(std::stoi(std::string{ std::next(nextArg), argIdEnd }));
571 if (argId)
572 result.args.emplace_back(argId.value(), result.text.size());
573 result.text.append(std::vformat(std::string_view{ nextArg, nextArgEnd }, std::make_format_args(std::forward<Args>(aArgs)...)));
574 if (argId)
575 result.args.back().end = result.text.size();
576 }
577 }
578 }
579 return result;
580 }
581}
std::string to_std_string() const
Definition i_string.hpp:75
void search(std::string const &aText, bool aRemoveSubmatches=true) const
void add_pattern(std::string const &aPattern, action_t aAction)
void push_back(const value_type &aValue) final
Definition string.hpp:108
CharT wildcard_match_any_character()
char16_t wildcard_match_any_string< char16_t >()
char16_t wildcard_match_any_character< char16_t >()
CharT wildcard_match_any_string()
int32_t string_to_int32(const std::basic_string_view< CharT, Traits > &aStringView)
std::string to_std_string(T const &aValue)
std::string & remove_leading(std::basic_string< CharT, Traits, Alloc > &aString, const std::basic_string< CharT, Traits, Alloc > &aLeading)
std::string parse_escapes(const std::string &aString)
FwdIter1 tokens(FwdIter1 aFirst, FwdIter1 aLast, FwdIter2 aDelimeterFirst, FwdIter2 aDelimiterLast, ResultContainer &aTokens, std::size_t aMaxTokens=0, bool aSkipEmptyTokens=true, bool aDelimeterIsSubsequence=false)
std::basic_string< CharT, Traits, Alloc >::size_type reverse_find_first_of(const std::basic_string< CharT, Traits, Alloc > &aString, const std::basic_string< CharT, Traits, Alloc > &aSequence, typename std::basic_string< CharT, Traits, Alloc >::size_type aPosition)
bool wildcard_match(FwdIter aTextBegin, FwdIter aTextEnd, FwdIter aPatternBegin, FwdIter aPatternEnd)
bool replace_string(std::basic_string< CharT, Traits, Alloc > &aString, const std::basic_string< CharT, Traits, Alloc > &aSearch, const std::basic_string< CharT, Traits, Alloc > &aReplace, string_spans *aSpans, const string_span::type *aNewSpanType)
std::string & remove_trailing(std::basic_string< CharT, Traits, Alloc > &aString, const std::basic_string< CharT, Traits, Alloc > &aTrailing)
std::vector< string_span > string_spans
std::basic_string< CharT, Traits, Alloc >::size_type reverse_find_last_of(const std::basic_string< CharT, Traits, Alloc > &aString, const std::basic_string< CharT, Traits, Alloc > &aSequence, typename std::basic_string< CharT, Traits, Alloc >::size_type aPosition)
T from_std_string(std::string const &aValueAsString)
T from_string(i_string const &aValueAsString)
format_result format(std::string_view const &aFormat, Args &&... aArgs)
std::basic_string< CharT, Traits, Alloc > to_lower(const std::basic_string< CharT, Traits, Alloc > &aString)
std::basic_string< CharT, Traits, Alloc > to_upper(const std::basic_string< CharT, Traits, Alloc > &aString)
std::string parse_url_escapes(const std::string &aString)
bool contains_character(const CharT *aSequence, CharT aCharacter)
bool do_wildcard_match(FwdIter aTextBegin, FwdIter aTextEnd, FwdIter aPatternBegin, FwdIter aPatternEnd)
std::string & remove_leading_and_trailing(std::basic_string< CharT, Traits, Alloc > &aString, const std::basic_string< CharT, Traits, Alloc > &aLeadingTrailing)
it_type next(it_type it, const typename iterator_traits< it_type >::difference_type distance=1)
Definition plf_hive.h:89
iterator_traits< it_type >::difference_type distance(const it_type first, const it_type last)
Definition plf_hive.h:107
void advance(it_type &it, const distance_type distance)
Definition plf_hive.h:81
comma_and_brackets_as_whitespace(std::size_t refs=0)
comma_as_whitespace(std::size_t refs=0)
static const mask * make_table()
comma_only_whitespace(std::size_t refs=0)
static const mask * make_table()
bool operator()(char_type c1, char_type c2) const
constexpr std::strong_ordering operator<=>(formatted_arg const &) const noexcept=default
std::string_view arg_span(std::size_t aArgIndex) const
arg_list::const_iterator find_arg(std::size_t aArgIndex) const
bool has_arg(std::size_t aArgIndex) const
std::vector< formatted_arg > arg_list
arg_list::const_iterator arg_after(std::ptrdiff_t aPos) const
arg_list::const_iterator arg_spanning(std::ptrdiff_t aPos) const
std::pair< std::size_t, std::size_t > span
string_span(const span &aSpan, type aType=0)
string_span(std::size_t aFirst, std::size_t aSecond, type aType=0)
string_span & operator=(const span &aSpan)