RAVENNAKIT
Loading...
Searching...
No Matches
string.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2/*
3 * Project: RAVENNAKIT (RAVENNA / AES67 / ST2110-30 SDK)
4 * Copyright (c) 2024-2025 Sound on Digital
5 *
6 * This file is part of RAVENNAKIT.
7 *
8 * RAVENNAKIT is dual-licensed:
9 * 1) Under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version (the "AGPL License"); and
12 * 2) Under a commercial license from Sound on Digital, for customers who
13 * cannot (or do not wish to) comply with the AGPL License terms.
14 *
15 * If you obtained this file under the AGPL License, you may redistribute it
16 * and/or modify it under the terms of the AGPL License. See the LICENSE
17 * file in the project root for details.
18 *
19 * For commercial licensing, support, and other inquiries, please visit:
20 *
21 * https://ravennakit.com
22 *
23 */
24
25#pragma once
26
27#include <algorithm>
28#include <charconv>
29#include <limits>
30#include <optional>
31#include <sstream>
32#include <string_view>
33#include <vector>
34
35namespace rav {
36
45inline std::string_view string_up_to_first_occurrence_of(
46 std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result
47) {
48 const auto pos = string_to_search_in.find(string_to_search_for);
49
50 if (pos == std::string_view::npos)
51 return {};
52
53 return string_to_search_in.substr(0, include_sub_string_in_result ? pos + string_to_search_for.size() : pos);
54}
55
65inline std::string_view string_up_to_the_nth_occurrence_of(
66 const size_t nth, std::string_view string_to_search_in, const std::string_view string_to_search_for,
67 const bool include_sub_string_in_result
68) {
69 size_t pos = std::string_view::npos;
70
71 for (size_t i = 0; i < nth; ++i) {
72 pos = string_to_search_in.find(string_to_search_for, pos + 1);
73
74 if (pos == std::string_view::npos) {
75 return {};
76 }
77 }
78
79 if (pos == std::string_view::npos) {
80 return {};
81 }
82
83 return string_to_search_in.substr(0, include_sub_string_in_result ? pos + string_to_search_for.size() : pos);
84}
85
94inline std::string_view string_up_to_last_occurrence_of(
95 const std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result
96) {
97 const auto pos = string_to_search_in.rfind(string_to_search_for);
98
99 if (pos == std::string_view::npos)
100 return {};
101
102 return string_to_search_in.substr(0, include_sub_string_in_result ? pos + string_to_search_for.size() : pos);
103}
104
113inline std::string_view string_from_first_occurrence_of(
114 std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result
115) {
116 const auto pos = string_to_search_in.find(string_to_search_for);
117
118 if (pos == std::string_view::npos)
119 return {};
120
121 return string_to_search_in.substr(include_sub_string_in_result ? pos : pos + string_to_search_for.size());
122}
123
133inline std::string_view string_from_nth_occurrence_of(
134 const size_t nth, std::string_view string_to_search_in, const std::string_view string_to_search_for,
135 const bool include_sub_string_in_result
136) {
137 size_t pos = std::string_view::npos;
138
139 for (size_t i = 0; i < nth; ++i) {
140 pos = string_to_search_in.find(string_to_search_for, pos + 1);
141
142 if (pos == std::string_view::npos) {
143 return {};
144 }
145 }
146
147 if (pos == std::string_view::npos) {
148 return {};
149 }
150
151 return string_to_search_in.substr(include_sub_string_in_result ? pos : pos + string_to_search_for.size(), string_to_search_in.size());
152}
153
162inline std::string_view string_from_last_occurrence_of(
163 const std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result
164) {
165 const auto pos = string_to_search_in.rfind(string_to_search_for);
166
167 if (pos == std::string_view::npos) {
168 return {};
169 }
170
171 return string_to_search_in.substr(include_sub_string_in_result ? pos : pos + string_to_search_for.size());
172}
173
181inline std::string_view
182string_remove_prefix(const std::string_view& string, const std::string_view prefix_to_remove, bool* found = nullptr) {
183 const auto pos = string.find(prefix_to_remove);
184
185 if (pos != 0) {
186 if (found) {
187 *found = false;
188 }
189 return string;
190 }
191
192 if (found) {
193 *found = true;
194 }
195
196 return string.substr(pos + prefix_to_remove.size());
197}
198
205inline std::string_view
206string_remove_suffix(const std::string_view& string, const std::string_view suffix_to_remove, bool* found = nullptr) {
207 const auto pos = string.rfind(suffix_to_remove);
208
209 if (pos != string.size() - suffix_to_remove.size()) {
210 if (found) {
211 *found = false;
212 }
213 return string;
214 }
215
216 if (found) {
217 *found = true;
218 }
219
220 return string.substr(0, pos);
221}
222
228inline std::string_view string_trim(const std::string_view input) {
229 if (input.empty()) {
230 return {};
231 }
232
233 // Find first non-whitespace character
234 size_t start = 0;
235 while (start < input.size() && std::isspace(input[start])) {
236 ++start;
237 }
238
239 // If all characters are whitespace
240 if (start == input.size()) {
241 return std::string_view {};
242 }
243
244 // Find last non-whitespace character
245 size_t end = input.size() - 1;
246 while (end > start && std::isspace(input[end])) {
247 --end;
248 }
249
250 return input.substr(start, end - start + 1);
251}
252
262template<typename Type>
263std::enable_if_t<std::is_integral_v<Type>, std::optional<Type>>
264string_to_int(std::string_view string, const bool strict = false, const int base = 10) {
265 Type result {};
266 auto [p, ec] = std::from_chars(string.data(), string.data() + string.size(), result, base);
267 if (ec == std::errc() && (!strict || p >= string.data() + string.size()))
268 return result;
269 return {};
270}
271
277inline std::optional<float> string_to_float(const std::string& str) {
278 try {
279 return std::stof(str);
280 } catch (...) {
281 return std::nullopt;
282 }
283}
284
290inline std::optional<double> string_to_double(const std::string& str) {
291 try {
292 return std::stod(str);
293 } catch (...) {
294 return std::nullopt;
295 }
296}
297
304inline bool string_starts_with(const std::string_view text, const std::string_view starts_with) {
305 return text.rfind(starts_with, 0) == 0;
306}
307
314inline bool string_ends_with(const std::string_view text, const std::string_view ends_with) {
315 if (ends_with.length() > text.length()) {
316 return false;
317 }
318 return text.compare(text.length() - ends_with.length(), ends_with.length(), ends_with) == 0;
319}
320
327inline bool string_contains(const std::string_view string, char c) {
328 return std::any_of(string.begin(), string.end(), [c](const char& item) {
329 return item == c;
330 });
331}
332
339inline bool string_contains(const std::string_view string, const std::string_view sub_string) {
340 return string.find(sub_string) != std::string_view::npos;
341}
342
349inline std::vector<std::string> string_split(const std::string& string, const char* delimiter) {
350 std::vector<std::string> results;
351
352 size_t prev = 0;
353 size_t next = 0;
354
355 while ((next = string.find_first_of(delimiter, prev)) != std::string::npos) {
356 if (next - prev != 0) {
357 results.push_back(string.substr(prev, next - prev));
358 }
359 prev = next + 1;
360 }
361
362 if (prev < string.size()) {
363 results.push_back(string.substr(prev));
364 }
365
366 return results;
367}
368
375inline std::vector<std::string> string_split(const std::string& string, const char delimiter) {
376 const char delimiter_string[2] = {delimiter, '\0'};
377 return string_split(string, delimiter_string);
378}
379
387inline std::string string_replace(const std::string_view original, const std::string_view to_replace, const std::string_view replacement) {
388 auto modified = std::string(original);
389 size_t pos = 0;
390
391 while ((pos = modified.find(to_replace, pos)) != std::string::npos) {
392 modified.replace(pos, to_replace.length(), replacement);
393 pos += replacement.length(); // Move past the replacement to avoid infinite loop
394 }
395
396 return modified;
397}
398
405inline std::string_view string_unquoted(std::string_view input) {
406 input = string_trim(input);
407
408 if (input.size() < 2) {
409 return input; // NOLINT
410 }
411
412 const char first = input.front();
413 const char last = input.back();
414
415 // Only unquote if there are matching quotes at both beginning AND end
416 if ((first == '"' && last == '"') || (first == '\'' && last == '\'')) {
417 return input.substr(1, input.size() - 2);
418 }
419
420 // If quotes don't match or aren't at both ends, return unchanged
421 return input; // NOLINT
422}
423
430inline bool string_compare_case_insensitive(const std::string_view lhs, const std::string_view rhs) {
431 if (lhs.size() != rhs.size()) {
432 return false;
433 }
434
435 for (size_t i = 0; i < lhs.size(); ++i) {
436 if (std::tolower(lhs[i]) != std::tolower(rhs[i])) {
437 return false;
438 }
439 }
440
441 return true;
442}
443
450inline std::string string_to_upper(const std::string& str, std::size_t count = std::numeric_limits<size_t>::max()) {
451 if (str.empty() || count == 0) {
452 return str;
453 }
454 std::string result = str;
455 count = std::min(count, result.size());
456 for (std::size_t i = 0; i < count; ++i) {
457 result[i] = static_cast<char>(std::toupper(static_cast<unsigned char>(result[i])));
458 }
459 return result;
460}
461
468inline std::string string_to_lower(const std::string& str, std::size_t count = std::numeric_limits<size_t>::max()) {
469 if (str.empty() || count == 0) {
470 return str;
471 }
472 std::string result = str;
473 count = std::min(count, result.size());
474 for (std::size_t i = 0; i < count; ++i) {
475 result[i] = static_cast<char>(std::tolower(static_cast<unsigned char>(result[i])));
476 }
477 return result;
478}
479
480} // namespace rav
std::string string_replace(const std::string_view original, const std::string_view to_replace, const std::string_view replacement)
Replaces all sequences of to_replace with replacement.
Definition string.hpp:387
std::string string_to_lower(const std::string &str, std::size_t count=std::numeric_limits< size_t >::max())
Converts the first count characters of a string to lower case.
Definition string.hpp:468
std::string_view string_from_first_occurrence_of(std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result)
Returns a string truncated starting from to first last occurrence of a needle.
Definition string.hpp:113
std::string_view string_trim(const std::string_view input)
Trims given string by removing whitespace at the start and the end or the string.
Definition string.hpp:228
std::enable_if_t< std::is_integral_v< Type >, std::optional< Type > > string_to_int(std::string_view string, const bool strict=false, const int base=10)
String to number - a small convenience function around std::from_chars.
Definition string.hpp:264
bool string_ends_with(const std::string_view text, const std::string_view ends_with)
Tests whether given text ends with a certain string.
Definition string.hpp:314
std::string_view string_up_to_first_occurrence_of(std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result)
Returns a string truncated up to the first occurrence of a needle.
Definition string.hpp:45
std::string_view string_remove_prefix(const std::string_view &string, const std::string_view prefix_to_remove, bool *found=nullptr)
Returns a copy of string with given prefix removed, if prefix is found at the beginning of the string...
Definition string.hpp:182
bool string_starts_with(const std::string_view text, const std::string_view starts_with)
Tests whether given text starts with a certain string.
Definition string.hpp:304
std::string string_to_upper(const std::string &str, std::size_t count=std::numeric_limits< size_t >::max())
Converts the first count characters of a string to upper case.
Definition string.hpp:450
std::string_view string_from_last_occurrence_of(const std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result)
Returns a string truncated starting from to last occurrence of a needle.
Definition string.hpp:162
std::string_view string_up_to_last_occurrence_of(const std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result)
Returns a string truncated up to the last occurrence of a needle.
Definition string.hpp:94
bool string_compare_case_insensitive(const std::string_view lhs, const std::string_view rhs)
Compares 2 strings case-insensitively.
Definition string.hpp:430
std::optional< float > string_to_float(const std::string &str)
String to float - converts a string to a float, or returns an empty optional if the conversion failed...
Definition string.hpp:277
std::string_view string_up_to_the_nth_occurrence_of(const size_t nth, std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result)
Returns a string truncated up to the nth occurrence of a needle.
Definition string.hpp:65
std::string_view string_remove_suffix(const std::string_view &string, const std::string_view suffix_to_remove, bool *found=nullptr)
Returns a copy of string with given suffix removed, if suffix is found at the end of the string.
Definition string.hpp:206
std::string_view string_from_nth_occurrence_of(const size_t nth, std::string_view string_to_search_in, const std::string_view string_to_search_for, const bool include_sub_string_in_result)
Returns a string truncated from the nth occurrence of a needle.
Definition string.hpp:133
std::string_view string_unquoted(std::string_view input)
If given string is quoted, it will remove the quotes.
Definition string.hpp:405
bool string_contains(const std::string_view string, char c)
Returns whether given string contains a certain character.
Definition string.hpp:327
std::vector< std::string > string_split(const std::string &string, const char *delimiter)
Splits a string into a vector of strings based on a delimiter.
Definition string.hpp:349
std::optional< double > string_to_double(const std::string &str)
String to double - converts a string to a double, or returns an empty optional if the conversion fail...
Definition string.hpp:290