0.4.1
Loading...
Searching...
No Matches
StringUtil.hpp
Go to the documentation of this file.
1// This file is part of INSTINCT, the INS Toolkit for Integrated
2// Navigation Concepts and Training by the Institute of Navigation of
3// the University of Stuttgart, Germany.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9/// @file StringUtil.hpp
10/// @brief Utility functions for working with std::strings
11/// @author T. Topp (topp@ins.uni-stuttgart.de)
12/// @date 2020-09-16
13
14#pragma once
15
16#include <algorithm>
17#include <cctype>
18#include <cstdint>
19#include <locale>
20#include <vector>
21#include <string>
22#include <string_view>
23
24namespace NAV::str
25{
26/// @brief Trim from start (in place)
27/// @param[in, out] s The string to trim
28static inline void ltrim(std::string& s)
29{
30 if (!s.empty() && s[0] == '\n')
31 {
32 s.erase(0, 1);
33 }
34 s.erase(s.begin(), std::ranges::find_if(s, [](int ch) { return !std::isspace(ch); }));
35}
36
37/// @brief Trim from end (in place)
38/// @param[in, out] s The string to trim
39static inline void rtrim(std::string& s)
40{
41 if (!s.empty() && s[s.length() - 1] == '\n')
42 {
43 s.erase(s.length() - 1);
44 }
45 s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { // NOLINT(boost-use-ranges,modernize-use-ranges) // ranges::find_last_if is C++23 and not supported yet
46 return !std::isspace(ch);
47 })
48 .base(),
49 s.end());
50}
51
52/// @brief Trim from both ends (in place)
53/// @param[in, out] s The string to trim
54static inline void trim(std::string& s)
55{
56 ltrim(s);
57 rtrim(s);
58}
59
60/// @brief Trim from start (in place)
61/// @param[in, out] sv The string view to trim
62static inline void ltrim(std::string_view& sv)
63{
64 sv.remove_prefix(std::min(sv.find_first_not_of(' '), sv.size()));
65}
66
67/// @brief Trim from end (in place)
68/// @param[in, out] sv The string view to trim
69static inline void rtrim(std::string_view& sv)
70{
71 sv.remove_suffix(std::min(sv.size() - sv.find_last_not_of(' ') - 1, sv.size()));
72}
73
74/// @brief Trim from both ends (in place)
75/// @param[in, out] sv The string view to trim
76static inline void trim(std::string_view& sv)
77{
78 ltrim(sv);
79 rtrim(sv);
80}
81
82/// @brief Trim from start (copying)
83/// @param[in] s The string to trim
84/// @return The trimmed string
85static inline std::string ltrim_copy(std::string s)
86{
87 ltrim(s);
88 return s;
89}
90
91/// @brief Trim from end (copying)
92/// @param[in] s The string to trim
93/// @return The trimmed string
94static inline std::string rtrim_copy(std::string s)
95{
96 rtrim(s);
97 return s;
98}
99
100/// @brief Trim from both ends (copying)
101/// @param[in] s The string to trim
102/// @return The trimmed string
103static inline std::string trim_copy(std::string s)
104{
105 trim(s);
106 return s;
107}
108
109/// @brief Trim from start (copying)
110/// @param[in] sv The string view to trim
111/// @return The trimmed string
112static inline std::string_view ltrim_copy(std::string_view sv)
113{
114 ltrim(sv);
115 return sv;
116}
117
118/// @brief Trim from end (copying)
119/// @param[in] sv The string view to trim
120/// @return The trimmed string
121static inline std::string_view rtrim_copy(std::string_view sv)
122{
123 rtrim(sv);
124 return sv;
125}
126
127/// @brief Trim from both ends (copying)
128/// @param[in] sv The string view to trim
129/// @return The trimmed string
130static inline std::string_view trim_copy(std::string_view sv)
131{
132 trim(sv);
133 return sv;
134}
135
136/// @brief Enum for case sensitive tasks
137enum CaseSensitivity : uint8_t
138{
139 RespectCase, ///< Respect the case
140 IgnoreCase, ///< Ignore case
141};
142
143/// @brief Replaces the first occurrence of a search pattern with another sequence
144/// @param[in, out] str String to search in and return value
145/// @param[in] from String pattern to search for
146/// @param[in] to Replacement string
147/// @param[in] cs Case sensitivity
148/// @return True if something was replaced
149static inline bool replace(std::string& str, const std::string& from, const std::string& to, CaseSensitivity cs = RespectCase)
150{
151 if (from.empty())
152 {
153 return false;
154 }
155 auto it = std::search(str.begin(), str.end(), // NOLINT(boost-use-ranges,modernize-use-ranges) // ranges::search is C++23 and not supported yet
156 from.begin(), from.end(),
157 [cs](char ch1, char ch2) { return cs == RespectCase
158 ? ch1 == ch2
159 : std::toupper(ch1) == std::toupper(ch2); });
160
161 if (it == str.end())
162 {
163 return false;
164 }
165 auto start_pos = static_cast<size_t>(it - str.begin());
166 str.replace(start_pos, from.length(), to);
167 return true;
168}
169
170/// @brief Replaces all occurrence of a search pattern with another sequence
171/// @param[in, out] str String to search in and return value
172/// @param[in] from String pattern to search for
173/// @param[in] to Replacement string
174/// @param[in] cs Case sensitivity
175static inline void replaceAll(std::string& str, const std::string& from, const std::string& to, CaseSensitivity cs)
176{
177 while (replace(str, from, to, cs)) {}
178}
179
180/// @brief Replaces all occurrence of a search pattern with another sequence
181/// @param[in, out] str String to search in and return value
182/// @param[in] from String pattern to search for
183/// @param[in] to Replacement string
184static inline void replaceAll(std::string& str, const std::string& from, const std::string& to)
185{
186 std::string::size_type n = 0;
187 while ((n = str.find(from, n)) != std::string::npos)
188 {
189 str.replace(n, from.size(), to);
190 n += to.size();
191 }
192}
193
194/// @brief Replaces all occurrence of a search pattern with another sequence
195/// @param[in, out] str String to search in and return value
196/// @param[in] from String pattern to search for
197/// @param[in] to Replacement string
198/// @param[in] cs Case sensitivity
199/// @return The string with the replacements
200static inline std::string replaceAll_copy(std::string str, const std::string& from, const std::string& to, CaseSensitivity cs)
201{
202 replaceAll(str, from, to, cs);
203 return str;
204}
205
206/// @brief Replaces all occurrence of a search pattern with another sequence
207/// @param[in, out] str String to search in and return value
208/// @param[in] from String pattern to search for
209/// @param[in] to Replacement string
210/// @return The string with the replacements
211static inline std::string replaceAll_copy(std::string str, const std::string& from, const std::string& to)
212{
213 replaceAll(str, from, to);
214 return str;
215}
216
217/// @brief Splits a string into parts at a delimiter
218/// @param[in] str String to split
219/// @param[in] delimiter Sequence of characters to split at
220/// @return List with splitted parts
221static inline std::vector<std::string> split(const std::string& str, const std::string& delimiter)
222{
223 size_t pos_start = 0;
224 size_t pos_end = 0;
225 size_t delim_len = delimiter.length();
226 std::vector<std::string> res;
227
228 while ((pos_end = str.find(delimiter, pos_start)) != std::string::npos)
229 {
230 res.push_back(str.substr(pos_start, pos_end - pos_start));
231 pos_start = pos_end + delim_len;
232 }
233 res.push_back(str.substr(pos_start));
234 return res;
235}
236
237/// @brief Splits a string into parts at a delimiter
238/// @param[in] str String to split
239/// @param[in] delimiter Character to split at
240/// @return List with splitted parts
241static inline std::vector<std::string> split(const std::string& str, char delimiter)
242{
243 return split(str, std::string(1, delimiter));
244}
245
246/// @brief Splits a string into parts at a delimiter and removes empty entries
247/// @param[in] str String to split
248/// @param[in] delimiter Sequence of characters to split at
249/// @return List with splitted parts
250static inline std::vector<std::string> split_wo_empty(const std::string& str, const std::string& delimiter)
251{
252 size_t pos_start = 0;
253 size_t pos_end = 0;
254 size_t delim_len = delimiter.length();
255 std::vector<std::string> res;
256
257 while ((pos_end = str.find(delimiter, pos_start)) != std::string::npos)
258 {
259 if (pos_start != pos_end)
260 {
261 res.push_back(str.substr(pos_start, pos_end - pos_start));
262 }
263 pos_start = pos_end + delim_len;
264 while (pos_start < str.size() && str.find(delimiter, pos_start) == pos_start)
265 {
266 pos_start += delim_len;
267 }
268 }
269 if (pos_start != str.size())
270 {
271 res.push_back(str.substr(pos_start));
272 }
273 return res;
274}
275
276/// @brief Splits a string into parts at a delimiter and removes empty entries
277/// @param[in] str String to split
278/// @param[in] delimiter Character to split at
279/// @return List with splitted parts
280static inline std::vector<std::string> split_wo_empty(const std::string& str, char delimiter)
281{
282 return split_wo_empty(str, std::string(1, delimiter));
283}
284
285/// @brief Concept limiting the type to std::string and std::wstring, but also allowing convertible types like const char*
286template<typename T>
287concept StdString = std::convertible_to<T, std::string> || std::convertible_to<T, std::wstring>;
288
289/// @brief Interprets a value in the string str
290/// @tparam String std::string or std::wstring and also allowing convertible types like const char*
291/// @param str the string to convert
292/// @param default_value default value to take if an invalid argument is given
293/// @param pos address of an integer to store the number of characters processed
294/// @param base the number base
295/// @return Value corresponding to the content of str
296template<StdString String>
297int stoi(const String& str, int default_value, std::size_t* pos = nullptr, int base = 10) noexcept
298{
299 try
300 {
301 return std::stoi(str, pos, base);
302 }
303 catch (...) // NOLINT(bugprone-empty-catch)
304 {}
305
306 return default_value;
307}
308
309/// @brief Interprets a value in the string str
310/// @tparam String std::string or std::wstring and also allowing convertible types like const char*
311/// @param str the string to convert
312/// @param default_value default value to take if an invalid argument is given
313/// @param pos address of an integer to store the number of characters processed
314/// @param base the number base
315/// @return Value corresponding to the content of str
316template<StdString String>
317int64_t stol(const String& str, int64_t default_value, std::size_t* pos = nullptr, int base = 10) noexcept
318{
319 try
320 {
321 return std::stol(str, pos, base);
322 }
323 catch (...) // NOLINT(bugprone-empty-catch)
324 {}
325
326 return default_value;
327}
328
329/// @brief Interprets a value in the string str
330/// @tparam String std::string or std::wstring and also allowing convertible types like const char*
331/// @param str the string to convert
332/// @param default_value default value to take if an invalid argument is given
333/// @param pos address of an integer to store the number of characters processed
334/// @param base the number base
335/// @return Value corresponding to the content of str
336template<StdString String>
337uint64_t stoul(const String& str, uint64_t default_value, std::size_t* pos = nullptr, int base = 10) noexcept
338{
339 try
340 {
341 return std::stoul(str, pos, base);
342 }
343 catch (...) // NOLINT(bugprone-empty-catch)
344 {}
345
346 return default_value;
347}
348
349/// @brief Interprets a value in the string str
350/// @tparam String std::string or std::wstring and also allowing convertible types like const char*
351/// @param str the string to convert
352/// @param default_value default value to take if an invalid argument is given
353/// @param pos address of an integer to store the number of characters processed
354/// @param base the number base
355/// @return Value corresponding to the content of str
356template<StdString String>
357int64_t stoll(const String& str, int64_t default_value, std::size_t* pos = nullptr, int base = 10) noexcept
358{
359 try
360 {
361 return std::stoll(str, pos, base);
362 }
363 catch (...) // NOLINT(bugprone-empty-catch)
364 {}
365
366 return default_value;
367}
368
369/// @brief Interprets a value in the string str
370/// @tparam String std::string or std::wstring and also allowing convertible types like const char*
371/// @param str the string to convert
372/// @param default_value default value to take if an invalid argument is given
373/// @param pos address of an integer to store the number of characters processed
374/// @return Value corresponding to the content of str
375template<StdString String>
376float stof(const String& str, float default_value, std::size_t* pos = nullptr) noexcept
377{
378 try
379 {
380 return std::stof(str, pos);
381 }
382 catch (...) // NOLINT(bugprone-empty-catch)
383 {}
384
385 return default_value;
386}
387
388/// @brief Interprets a value in the string str
389/// @tparam String std::string or std::wstring and also allowing convertible types like const char*
390/// @param str the string to convert
391/// @param default_value default value to take if an invalid argument is given
392/// @param pos address of an integer to store the number of characters processed
393/// @return Value corresponding to the content of str
394template<StdString String>
395double stod(const String& str, double default_value, std::size_t* pos = nullptr) noexcept
396{
397 try
398 {
399 return std::stod(str, pos);
400 }
401 catch (...) // NOLINT(bugprone-empty-catch)
402 {}
403
404 return default_value;
405}
406
407/// @brief Interprets a value in the string str
408/// @tparam String std::string or std::wstring and also allowing convertible types like const char*
409/// @param str the string to convert
410/// @param default_value default value to take if an invalid argument is given
411/// @param pos address of an integer to store the number of characters processed
412/// @return Value corresponding to the content of str
413template<StdString String>
414long double stold(const String& str, long double default_value, std::size_t* pos = nullptr) noexcept
415{
416 try
417 {
418 return std::stold(str, pos);
419 }
420 catch (...) // NOLINT(bugprone-empty-catch)
421 {}
422
423 return default_value;
424}
425
426} // namespace NAV::str
Concept limiting the type to std::string and std::wstring, but also allowing convertible types like c...
int stoi(const String &str, int default_value, std::size_t *pos=nullptr, int base=10) noexcept
Interprets a value in the string str.
double stod(const String &str, double default_value, std::size_t *pos=nullptr) noexcept
Interprets a value in the string str.
long double stold(const String &str, long double default_value, std::size_t *pos=nullptr) noexcept
Interprets a value in the string str.
static std::string trim_copy(std::string s)
Trim from both ends (copying)
static std::string ltrim_copy(std::string s)
Trim from start (copying)
static void rtrim(std::string &s)
Trim from end (in place)
uint64_t stoul(const String &str, uint64_t default_value, std::size_t *pos=nullptr, int base=10) noexcept
Interprets a value in the string str.
static void ltrim(std::string &s)
Trim from start (in place)
static void trim(std::string &s)
Trim from both ends (in place)
int64_t stoll(const String &str, int64_t default_value, std::size_t *pos=nullptr, int base=10) noexcept
Interprets a value in the string str.
static std::string replaceAll_copy(std::string str, const std::string &from, const std::string &to, CaseSensitivity cs)
Replaces all occurrence of a search pattern with another sequence.
float stof(const String &str, float default_value, std::size_t *pos=nullptr) noexcept
Interprets a value in the string str.
static std::string rtrim_copy(std::string s)
Trim from end (copying)
static void replaceAll(std::string &str, const std::string &from, const std::string &to, CaseSensitivity cs)
Replaces all occurrence of a search pattern with another sequence.
static std::vector< std::string > split_wo_empty(const std::string &str, const std::string &delimiter)
Splits a string into parts at a delimiter and removes empty entries.
CaseSensitivity
Enum for case sensitive tasks.
@ RespectCase
Respect the case.
@ IgnoreCase
Ignore case.
static std::vector< std::string > split(const std::string &str, const std::string &delimiter)
Splits a string into parts at a delimiter.
int64_t stol(const String &str, int64_t default_value, std::size_t *pos=nullptr, int base=10) noexcept
Interprets a value in the string str.
static bool replace(std::string &str, const std::string &from, const std::string &to, CaseSensitivity cs=RespectCase)
Replaces the first occurrence of a search pattern with another sequence.