OpenMS
Loading...
Searching...
No Matches
StringUtils.h
Go to the documentation of this file.
1// Copyright (c) 2002-present, OpenMS Inc. -- EKU Tuebingen, ETH Zurich, and FU Berlin
2// SPDX-License-Identifier: BSD-3-Clause
3//
4// --------------------------------------------------------------------------
5// $Maintainer: Timo Sachsenberg, Chris Bielow $
6// $Authors: Marc Sturm, Stephan Aiche, Chris Bielow $
7// --------------------------------------------------------------------------
8
9#pragma once
10
11// NOTE: Do NOT include String.h here — String.h includes us. Use std::string directly.
12
15#include <OpenMS/OpenMSConfig.h>
16
17#include <boost/spirit/include/qi.hpp>
18#include <boost/spirit/include/karma.hpp>
19#include <boost/spirit/home/karma/numeric/detail/real_utils.hpp>
20#include <boost/type_traits.hpp>
21#include <boost/functional/hash.hpp>
22
23#include <algorithm>
24#include <cmath>
25#include <cstdio>
26#include <cstring>
27#include <limits>
28#include <ranges>
29#include <sstream>
30#include <string>
31#include <string_view>
32#include <vector>
33
34namespace OpenMS
35{
36 class DataValue; // forward — DataValue.h includes String.h, so we cannot include it here
37 class ParamValue; // forward — same reason
38
39 // -------------------------------------------------------------------------
40 // QuotingMethod — formerly String::QuotingMethod
41 // -------------------------------------------------------------------------
43 enum class QuotingMethod { NONE, ESCAPE, DOUBLE };
44
45
46 // =========================================================================
47 // Internal Boost.Karma generator policies (precision, NaN handling)
48 // =========================================================================
49 namespace StringConversions
50 {
51 namespace Detail
52 {
54 template<typename T>
55 class BK_PrecPolicyFull : public boost::spirit::karma::real_policies<T>
56 {
57 typedef boost::spirit::karma::real_policies<T> base_policy_type;
58 public:
59 static unsigned precision(T /*n*/) { return writtenDigits<T>(); }
60
61 static unsigned floatfield(T n)
62 {
63 if (boost::spirit::traits::test_zero(n)) return base_policy_type::fmtflags::fixed;
64 T abs_n = boost::spirit::traits::get_absolute_value(n);
65 // downstream tools (Java-based) read only up to 19 digits — keep scientific for large/small values
66 return (abs_n >= 1e4 || abs_n < 1e-2) ? base_policy_type::fmtflags::scientific
67 : base_policy_type::fmtflags::fixed;
68 }
69
70 template<typename CharEncoding, typename Tag, typename OutputIterator>
71 static bool nan(OutputIterator& sink, T n, bool force_sign)
72 {
73 return boost::spirit::karma::sign_inserter::call(
74 sink, false, boost::spirit::traits::test_negative(n), force_sign)
75 && boost::spirit::karma::string_inserter<CharEncoding, Tag>::call(sink, "NaN");
76 }
77 };
78
80 template<typename T>
81 class BK_PrecPolicyShort : public boost::spirit::karma::real_policies<T>
82 {
83 typedef boost::spirit::karma::real_policies<T> base_policy_type;
84 public:
85 template<typename CharEncoding, typename Tag, typename OutputIterator>
86 static bool nan(OutputIterator& sink, T n, bool force_sign)
87 {
88 return boost::spirit::karma::sign_inserter::call(
89 sink, false, boost::spirit::traits::test_negative(n), force_sign)
90 && boost::spirit::karma::string_inserter<CharEncoding, Tag>::call(sink, "NaN");
91 }
92 };
93
94 using FloatFullGen = boost::spirit::karma::real_generator<float, BK_PrecPolicyFull<float>>;
95 using DoubleFullGen = boost::spirit::karma::real_generator<double, BK_PrecPolicyFull<double>>;
96 using LDFullGen = boost::spirit::karma::real_generator<long double, BK_PrecPolicyFull<long double>>;
97
98 using FloatShortGen = boost::spirit::karma::real_generator<float, BK_PrecPolicyShort<float>>;
99 using DoubleShortGen = boost::spirit::karma::real_generator<double, BK_PrecPolicyShort<double>>;
100 using LDShortGen = boost::spirit::karma::real_generator<long double, BK_PrecPolicyShort<long double>>;
101
102 inline const FloatFullGen floatFull{};
103 inline const DoubleFullGen doubleFull{};
104 inline const LDFullGen ldFull{};
105 inline const FloatShortGen floatShort{};
107 inline const LDShortGen ldShort{};
108 } // namespace Detail
109 } // namespace StringConversions
110
111
112 // =========================================================================
113 // Internal Boost.Spirit.Qi parser helper (NaN-safe)
114 // =========================================================================
115 class OPENMS_DLLAPI StringUtilsHelper
116 {
117 public:
119 static Int32 toInt32(const std::string& s);
121 static Int64 toInt64(const std::string& s);
123 static float toFloat(const std::string& s);
125 static double toDouble(const std::string& s);
126
129 template <typename IteratorT>
130 static bool extractDouble(IteratorT& begin, const IteratorT& end, double& target)
131 {
132 return boost::spirit::qi::parse(begin, end, parse_double_, target);
133 }
134
137 template <typename IteratorT>
138 static bool extractInt(IteratorT& begin, const IteratorT& end, int& target)
139 {
140 return boost::spirit::qi::parse(begin, end, parse_int_, target);
141 }
142
143 private:
144 template <typename T>
146 {
147 template <typename Iterator, typename Attribute>
148 static bool parse_nan(Iterator& first, Iterator const& last, Attribute& attr_)
149 {
150 if (first == last) return false;
151 if (*first != 'n' && *first != 'N') return false;
152 if (boost::spirit::qi::detail::string_parse("nan", "NAN", first, last, boost::spirit::qi::unused))
153 {
154 if (first != last && *first == '(')
155 {
156 Iterator i = first;
157 while (++i != last && *i != ')') ;
158 if (i == last) return false;
159 first = ++i;
160 }
161 attr_ = std::numeric_limits<T>::quiet_NaN();
162 return true;
163 }
164 return false;
165 }
166 };
167
168 static boost::spirit::qi::real_parser<double, real_policies_NANfixed_<double>> parse_double_;
169 static boost::spirit::qi::real_parser<float, real_policies_NANfixed_<float>> parse_float_;
170 static boost::spirit::qi::int_parser<> parse_int_;
171 };
172
173
174 // =========================================================================
175 // StringUtils — unified string utility namespace
176 // =========================================================================
177 namespace StringUtils
178 {
179
180 // -----------------------------------------------------------------------
181 // Append numeric value to a string (no copy; fastest path)
182 // -----------------------------------------------------------------------
183
185 template <typename T>
186 inline void appendToStr(const T& i, std::string& target)
187 {
188 std::back_insert_iterator<std::string> sink(target);
189 boost::spirit::karma::generate(sink, i);
190 }
191
193 inline void appendToStr(int i, std::string& target)
194 { appendToStr<int>(i, target); }
195 inline void appendToStr(unsigned int i, std::string& target)
196 { appendToStr<unsigned int>(i, target); }
197 inline void appendToStr(short int i, std::string& target)
198 { appendToStr<short int>(i, target); }
199 inline void appendToStr(short unsigned int i, std::string& target)
200 { appendToStr<short unsigned int>(i, target); }
201 inline void appendToStr(long int i, std::string& target)
202 { appendToStr<long int>(i, target); }
203 inline void appendToStr(long unsigned int i, std::string& target)
204 { appendToStr<long unsigned int>(i, target); }
205 inline void appendToStr(long long unsigned int i, std::string& target)
206 { appendToStr<long long unsigned int>(i, target); }
207 inline void appendToStr(long long signed int i, std::string& target)
208 { appendToStr<long long signed int>(i, target); }
209
211 inline void appendToStr(float f, std::string& target)
212 {
213 std::back_insert_iterator<std::string> sink(target);
214 boost::spirit::karma::generate(sink, StringConversions::Detail::floatFull, f);
215 }
217 inline void appendToStrLowP(float f, std::string& target)
218 {
219 std::back_insert_iterator<std::string> sink(target);
220 boost::spirit::karma::generate(sink, StringConversions::Detail::floatShort, f);
221 }
222
224 inline void appendToStr(double d, std::string& target)
225 {
226 std::back_insert_iterator<std::string> sink(target);
227 boost::spirit::karma::generate(sink, StringConversions::Detail::doubleFull, d);
228 }
230 inline void appendToStrLowP(double d, std::string& target)
231 {
232 std::back_insert_iterator<std::string> sink(target);
233 boost::spirit::karma::generate(sink, StringConversions::Detail::doubleShort, d);
234 }
235
237 inline void appendToStr(long double ld, std::string& target)
238 {
239 std::back_insert_iterator<std::string> sink(target);
240 boost::spirit::karma::generate(sink, StringConversions::Detail::ldFull, ld);
241 }
243 inline void appendToStrLowP(long double ld, std::string& target)
244 {
245 std::back_insert_iterator<std::string> sink(target);
246 boost::spirit::karma::generate(sink, StringConversions::Detail::ldShort, ld);
247 }
248
250 OPENMS_DLLAPI void appendToStr(const DataValue& d, bool full_precision, std::string& target);
251
252
253 // -----------------------------------------------------------------------
254 // toStr() — convert a value to a new std::string
255 // -----------------------------------------------------------------------
256
257 inline std::string toStr(int i)
258 { std::string s; appendToStr(i, s); return s; }
259 inline std::string toStr(unsigned int i)
260 { std::string s; appendToStr(i, s); return s; }
261 inline std::string toStr(short int i)
262 { std::string s; appendToStr(i, s); return s; }
263 inline std::string toStr(short unsigned int i)
264 { std::string s; appendToStr(i, s); return s; }
265 inline std::string toStr(long int i)
266 { std::string s; appendToStr(i, s); return s; }
267 inline std::string toStr(long unsigned int i)
268 { std::string s; appendToStr(i, s); return s; }
269 inline std::string toStr(long long unsigned int i)
270 { std::string s; appendToStr(i, s); return s; }
271 inline std::string toStr(long long signed int i)
272 { std::string s; appendToStr(i, s); return s; }
273
275 inline std::string toStr(float f, bool full_precision = true)
276 {
277 std::string s;
278 full_precision ? appendToStr(f, s) : appendToStrLowP(f, s);
279 return s;
280 }
282 inline std::string toStr(double d, bool full_precision = true)
283 {
284 std::string s;
285 full_precision ? appendToStr(d, s) : appendToStrLowP(d, s);
286 return s;
287 }
289 inline std::string toStr(long double ld, bool full_precision = true)
290 {
291 std::string s;
292 full_precision ? appendToStr(ld, s) : appendToStrLowP(ld, s);
293 return s;
294 }
295
296 inline std::string toStr(char c)
297 { return std::string(1, c); }
298
299 inline std::string toStr(const std::string& s)
300 { return s; }
301
302 inline std::string toStr(const char* s)
303 { return std::string(s); }
304
326 OPENMS_DLLAPI std::string toStr(const DataValue& d, bool full_precision = true);
327
329 OPENMS_DLLAPI std::string toStr(const ParamValue& p, bool full_precision = true);
330
331
332 // -----------------------------------------------------------------------
333 // Parse string → numeric
334 // -----------------------------------------------------------------------
335
336 inline Int32 toInt32(const std::string& s) { return StringUtilsHelper::toInt32(s); }
337 inline Int64 toInt64(const std::string& s) { return StringUtilsHelper::toInt64(s); }
338 inline float toFloat(const std::string& s) { return StringUtilsHelper::toFloat(s); }
339 inline double toDouble(const std::string& s) { return StringUtilsHelper::toDouble(s); }
340
341 template <typename IteratorT>
342 inline bool extractDouble(IteratorT& begin, const IteratorT& end, double& target)
343 { return StringUtilsHelper::extractDouble(begin, end, target); }
344
345 template <typename IteratorT>
346 inline bool extractInt(IteratorT& begin, const IteratorT& end, int& target)
347 { return StringUtilsHelper::extractInt(begin, end, target); }
348
349
350 // -----------------------------------------------------------------------
351 // Whitespace scanning (SIMD-accelerated; non-inline)
352 // -----------------------------------------------------------------------
353
355 OPENMS_DLLAPI const char* skipWhitespace(const char* p, const char* p_end);
356
358 inline int skipWhitespace(const std::string_view& data)
359 {
360 auto pos = skipWhitespace(data.data(), data.data() + data.size());
361 return static_cast<int>(pos - data.data());
362 }
363
365 OPENMS_DLLAPI const char* skipNonWhitespace(const char* p, const char* p_end);
366
368 inline int skipNonWhitespace(const std::string_view& data)
369 {
370 auto pos = skipNonWhitespace(data.data(), data.data() + data.size());
371 return static_cast<int>(pos - data.data());
372 }
373
374
375 // -----------------------------------------------------------------------
376 // Static factory methods
377 // -----------------------------------------------------------------------
378
380 [[maybe_unused]] inline std::string number(double d, UInt n)
381 {
382 char buf[64];
383 std::snprintf(buf, sizeof(buf), "%.*f", static_cast<int>(n), d);
384 return std::string(buf);
385 }
386
388 [[maybe_unused]] inline std::string numberLength(double d, UInt n)
389 {
390 std::stringstream s;
391 Int sign = (d < 0) ? 1 : 0;
392 d = std::fabs(d);
393 if (d < std::pow(10.0, Int(n - sign - 2)))
394 {
395 s.precision(writtenDigits(d));
396 if (sign == 1) s << '-';
397 s << d;
398 }
399 else
400 {
401 UInt exp = 0;
402 while (d > std::pow(10.0, Int(n - sign - 4)))
403 {
404 d /= 10;
405 ++exp;
406 }
407 d = static_cast<int>(d) / 10.0;
408 exp += 1;
409 if (sign == 1) s << '-';
410 s << d << 'e';
411 if (exp < 10) s << '0';
412 s << exp;
413 }
414 return s.str().substr(0, n);
415 }
416
418 inline std::string random(UInt length)
419 {
420 srand(time(nullptr));
421 std::string tmp(length, '.');
422 for (Size i = 0; i < length; ++i)
423 {
424 size_t r = static_cast<size_t>(
425 std::floor((static_cast<double>(rand()) / (double(RAND_MAX) + 1)) * 62.0));
426 if (r < 10) tmp[i] = static_cast<char>(r + 48);
427 else if (r < 36) tmp[i] = static_cast<char>(r + 55);
428 else tmp[i] = static_cast<char>(r + 61);
429 }
430 return tmp;
431 }
432
433
434 // -----------------------------------------------------------------------
435 // Predicates
436 // -----------------------------------------------------------------------
437
438 inline bool hasPrefix(const std::string& s, const std::string& prefix)
439 {
440 if (prefix.size() > s.size()) return false;
441 if (prefix.empty()) return true;
442 return s.compare(0, prefix.size(), prefix) == 0;
443 }
444 inline bool hasPrefix(const std::string& s, char c)
445 { return !s.empty() && s.front() == c; }
446
447 inline bool hasSuffix(const std::string& s, const std::string& sfx)
448 {
449 if (sfx.size() > s.size()) return false;
450 if (sfx.empty()) return true;
451 return s.compare(s.size() - sfx.size(), sfx.size(), sfx) == 0;
452 }
453 inline bool hasSuffix(const std::string& s, char c)
454 { return !s.empty() && s.back() == c; }
455
456 inline bool hasSubstring(const std::string& s, const std::string& sub)
457 {
458 return s.find(sub) != std::string::npos;
459 }
460 inline bool hasSubstring(const std::string& s, char c)
461 { return s.find(c) != std::string::npos; }
462
463 inline bool has(const std::string& s, Byte byte)
464 {
465 return s.find(static_cast<char>(byte)) != std::string::npos;
466 }
467 inline bool has(const std::string& s, char c)
468 { return s.find(c) != std::string::npos; }
469
470
471 // -----------------------------------------------------------------------
472 // Accessors (return new string)
473 // -----------------------------------------------------------------------
474
475 inline std::string prefix(const std::string& s, size_t length)
476 {
477 if (length > s.size())
478 throw Exception::IndexOverflow(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION, length, s.size());
479 return s.substr(0, length);
480 }
481
482 inline std::string suffix(const std::string& s, size_t length)
483 {
484 if (length > s.size())
485 throw Exception::IndexOverflow(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION, length, s.size());
486 return s.substr(s.size() - length, length);
487 }
488
489 inline std::string prefix(const std::string& s, Int length)
490 {
491 if (length < 0)
492 throw Exception::IndexUnderflow(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION, length, 0);
493 return prefix(s, static_cast<size_t>(length));
494 }
495
496 inline std::string suffix(const std::string& s, Int length)
497 {
498 if (length < 0)
499 throw Exception::IndexUnderflow(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION, length, 0);
500 return suffix(s, static_cast<size_t>(length));
501 }
502
508 inline std::string prefix(const std::string& s, char delim)
509 {
510 return s.substr(0, s.find(delim)); // find() returns npos if absent -> substr(0, npos) -> whole string
511 }
512
518 inline std::string suffix(const std::string& s, char delim)
519 {
520 size_t pos = s.rfind(delim);
521 return pos == std::string::npos ? s : s.substr(pos + 1);
522 }
523
525 inline std::string substr(const std::string& s, size_t pos = 0, size_t n = std::string::npos)
526 {
527 size_t begin = std::min(pos, s.size());
528 return s.substr(begin, n);
529 }
531 inline std::string substr(std::string_view s, size_t pos = 0, size_t n = std::string::npos)
532 {
533 size_t begin = std::min(pos, s.size());
534 return std::string(s.substr(begin, n));
535 }
536
538 inline std::string toStr(std::string_view sv) { return std::string(sv); }
539
541 inline bool hasPrefix(std::string_view s, const std::string& prefix)
542 { return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix; }
543 inline bool hasSuffix(std::string_view s, const std::string& sfx)
544 { return s.size() >= sfx.size() && s.substr(s.size() - sfx.size()) == sfx; }
545 inline bool hasSubstring(std::string_view s, const std::string& sub)
546 { return s.find(sub) != std::string_view::npos; }
547
549 inline std::string chop(const std::string& s, Size n)
550 {
551 size_t end = (n < s.size()) ? s.size() - n : 0;
552 return std::string(s.begin(), s.begin() + end);
553 }
554
555
556 // -----------------------------------------------------------------------
557 // In-place mutators (all return std::string& for chaining via free fns)
558 // -----------------------------------------------------------------------
559
560 inline std::string& trim(std::string& s)
561 {
562 auto begin = s.begin();
563 while (begin != s.end() && (*begin == ' ' || *begin == '\t' || *begin == '\n' || *begin == '\r'))
564 ++begin;
565 if (begin == s.end()) { s.clear(); return s; }
566 auto end = s.end() - 1;
567 while (end != begin && (*end == ' ' || *end == '\n' || *end == '\t' || *end == '\r'))
568 --end;
569 ++end;
570 if (begin != s.begin() || end != s.end())
571 s.assign(begin, end);
572 return s;
573 }
574
576 inline std::string trimmed(std::string s) { return std::move(trim(s)); }
577
578 inline std::string& toUpper(std::string& s)
579 {
580 std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::toupper(c); });
581 return s;
582 }
583
584 inline std::string& toLower(std::string& s)
585 {
586 std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::tolower(c); });
587 return s;
588 }
590 inline std::string toUppered(std::string s) { return std::move(toUpper(s)); }
592 inline std::string toLowered(std::string s) { return std::move(toLower(s)); }
593
594 inline std::string& firstToUpper(std::string& s)
595 {
596 if (!s.empty()) s[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(s[0])));
597 return s;
598 }
599
600 inline std::string& reverse(std::string& s)
601 {
602 std::reverse(s.begin(), s.end());
603 return s;
604 }
605
606 inline std::string& simplify(std::string& s)
607 {
608 std::string result;
609 result.reserve(s.size());
610 bool last_ws = false;
611 for (char c : s)
612 {
613 if (c == ' ' || c == '\n' || c == '\t' || c == '\r')
614 {
615 if (!last_ws) result += ' ';
616 last_ws = true;
617 }
618 else
619 {
620 result += c;
621 last_ws = false;
622 }
623 }
624 s.swap(result);
625 return s;
626 }
627
628 inline std::string& fillLeft(std::string& s, char c, UInt size)
629 {
630 if (s.size() < size)
631 s.insert(s.begin(), size - s.size(), c);
632 return s;
633 }
634
635 inline std::string& fillRight(std::string& s, char c, UInt size)
636 {
637 if (s.size() < size)
638 s.append(size - s.size(), c);
639 return s;
640 }
641
642 inline std::string& substitute(std::string& s, char from, char to)
643 {
644 std::replace(s.begin(), s.end(), from, to);
645 return s;
646 }
647
649 inline std::string& substitute(std::string& s, const std::string& from, const std::string& to)
650 {
651 if (from.empty()) return s;
652 std::string result;
653 result.reserve(s.size());
654 size_t start = 0;
655 size_t pos;
656 while ((pos = s.find(from, start)) != std::string::npos)
657 {
658 result.append(s, start, pos - start);
659 result += to;
660 start = pos + from.size();
661 }
662 result.append(s, start, std::string::npos);
663 s.swap(result);
664 return s;
665 }
667 inline std::string substituted(std::string s, char from, char to) { return std::move(substitute(s, from, to)); }
668 inline std::string substituted(std::string s, const std::string& from, const std::string& to) { return std::move(substitute(s, from, to)); }
669
670 inline std::string& remove(std::string& s, char what)
671 {
672 s.erase(std::remove(s.begin(), s.end(), what), s.end());
673 return s;
674 }
675
676 // rvalue-ref overloads so chained/temporary strings can be padded in place
677 inline std::string fillLeft(std::string&& s, char c, UInt size) { fillLeft(s, c, size); return std::move(s); }
678 inline std::string fillRight(std::string&& s, char c, UInt size) { fillRight(s, c, size); return std::move(s); }
679
680 inline std::string& ensureLastChar(std::string& s, char end)
681 {
682 if (s.empty() || s.back() != end) s.push_back(end);
683 return s;
684 }
686 inline std::string ensureLastChar(std::string&& s, char end) { ensureLastChar(s, end); return std::move(s); }
687
688 inline std::string& removeWhitespaces(std::string& s)
689 {
690 // skip unmodified prefix
691 int start = skipNonWhitespace(std::string_view(s.data(), s.size()));
692 auto it = s.cbegin() + start;
693 auto dest = s.begin() + start;
694 auto it_end = s.cend();
695 bool has_spaces = false;
696 while (it != it_end)
697 {
698 const char c = *it;
699 if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
700 {
701 ++it;
702 has_spaces = true;
703 continue;
704 }
705 if (has_spaces) *dest = *it;
706 ++dest;
707 ++it;
708 }
709 if (has_spaces) s.resize(static_cast<size_t>(dest - s.begin()));
710 return s;
711 }
712
713 inline bool isQuoted(const std::string& s, char q)
714 {
715 return s.size() >= 2 && s.front() == q && s.back() == q;
716 }
717
718 inline std::string& quote(std::string& s, char q = '"', QuotingMethod method = QuotingMethod::ESCAPE)
719 {
720 if (method == QuotingMethod::ESCAPE)
721 {
722 substitute(s, std::string("\\"), std::string("\\\\"));
723 substitute(s, std::string(1, q), std::string("\\") + q);
724 }
725 else if (method == QuotingMethod::DOUBLE)
726 {
727 substitute(s, std::string(1, q), std::string(2, q));
728 }
729 s.insert(s.begin(), q);
730 s.push_back(q);
731 return s;
732 }
733
734 inline std::string& unquote(std::string& s, char q = '"', QuotingMethod method = QuotingMethod::ESCAPE)
735 {
736 if (!isQuoted(s, q))
737 throw Exception::ConversionError(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION,
738 "'" + s + "' does not have the expected format of a quoted string");
739 s.erase(s.begin());
740 s.pop_back();
741 if (method == QuotingMethod::ESCAPE)
742 {
743 substitute(s, std::string("\\") + q, std::string(1, q));
744 substitute(s, std::string("\\\\"), std::string("\\"));
745 }
746 else if (method == QuotingMethod::DOUBLE)
747 {
748 substitute(s, std::string(2, q), std::string(1, q));
749 }
750 return s;
751 }
752
753
754 // -----------------------------------------------------------------------
755 // Split / Join
756 // -----------------------------------------------------------------------
757
761 inline bool split(const std::string& s, char splitter,
762 std::vector<std::string>& substrings,
763 bool quote_protect = false)
764 {
765 substrings.clear();
766 if (s.empty()) return false;
767
768 size_t nsplits = static_cast<size_t>(std::count(s.begin(), s.end(), splitter));
769
770 if (!quote_protect && nsplits == 0)
771 {
772 substrings.push_back(s);
773 return false;
774 }
775
776 substrings.reserve(nsplits + 1);
777
778 if (quote_protect)
779 {
780 int quote_count = 0;
781 auto begin = s.cbegin();
782 auto end = s.cbegin();
783 for (; end != s.cend(); ++end)
784 {
785 if (*end == '"') ++quote_count;
786 if ((quote_count % 2 == 0) && (*end == splitter))
787 {
788 std::string block(begin, end);
789 trim(block);
790 bool has_start = block.size() >= 1 && block.front() == '"';
791 bool has_end = block.size() >= 1 && block.back() == '"';
792 if (block.size() >= 2 && (has_start ^ has_end))
793 throw Exception::ConversionError(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION,
794 "Could not dequote string '" + block + "' due to wrongly placed '\"'.");
795 if (block.size() >= 2 && has_start && has_end)
796 block = block.substr(1, block.size() - 2);
797 substrings.push_back(std::move(block));
798 begin = end + 1;
799 }
800 }
801 if (substrings.empty()) { substrings.push_back(s); return false; }
802 std::string block(begin, end);
803 trim(block);
804 bool has_start = block.size() >= 1 && block.front() == '"';
805 bool has_end = block.size() >= 1 && block.back() == '"';
806 if (block.size() >= 2 && (has_start ^ has_end))
807 throw Exception::ConversionError(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION,
808 "Could not dequote string '" + block + "' due to wrongly placed '\"'.");
809 if (block.size() >= 2 && has_start && has_end)
810 block = block.substr(1, block.size() - 2);
811 substrings.push_back(std::move(block));
812 }
813 else
814 {
815 auto begin = s.cbegin();
816 for (auto it = s.cbegin(); it != s.cend(); ++it)
817 {
818 if (*it == splitter)
819 {
820 substrings.emplace_back(begin, it);
821 begin = it + 1;
822 }
823 }
824 substrings.emplace_back(begin, s.cend());
825 }
826 return true;
827 }
828
831 inline bool split(const std::string& s, const std::string& splitter,
832 std::vector<std::string>& substrings)
833 {
834 substrings.clear();
835 if (s.empty()) return false;
836 if (splitter.empty())
837 {
838 substrings.resize(s.size());
839 for (size_t i = 0; i < s.size(); ++i) substrings[i] = std::string(1, s[i]);
840 return true;
841 }
842 size_t len = splitter.size();
843 size_t start = 0;
844 size_t pos = s.find(splitter);
845 while (pos != std::string::npos)
846 {
847 substrings.push_back(s.substr(start, pos - start));
848 start = pos + len;
849 pos = s.find(splitter, start);
850 }
851 substrings.push_back(s.substr(start));
852 return substrings.size() > 1;
853 }
854
857 inline bool split_quoted(const std::string& s, const std::string& splitter,
858 std::vector<std::string>& substrings,
859 char q = '"', QuotingMethod method = QuotingMethod::ESCAPE)
860 {
861 substrings.clear();
862 if (s.empty() || splitter.empty()) return false;
863
864 bool in_quote = false;
865 char targets[2] = {q, splitter[0]};
866 std::string rest = splitter.substr(1);
867 size_t start = 0;
868
869 for (size_t i = 0; i < s.size(); ++i)
870 {
871 if (in_quote)
872 {
873 bool embedded = false;
874 if (method == QuotingMethod::ESCAPE)
875 {
876 for (; i < s.size(); ++i)
877 {
878 if (s[i] == '\\') embedded = !embedded;
879 else if (s[i] == q && !embedded) break;
880 else embedded = false;
881 }
882 }
883 else
884 {
885 for (; i < s.size(); ++i)
886 {
887 if (s[i] == q)
888 {
889 if (method == QuotingMethod::NONE) break;
890 if (i + 1 < s.size() && s[i + 1] == q) embedded = !embedded;
891 else if (!embedded) break;
892 else embedded = false;
893 }
894 }
895 }
896 in_quote = false;
897 }
898 else
899 {
900 i = s.find_first_of(targets, i, 2);
901 if (i == std::string::npos) break;
902 if (s[i] == q) { in_quote = true; }
903 else if (s.compare(i + 1, rest.size(), rest) == 0)
904 {
905 substrings.push_back(s.substr(start, i - start));
906 start = i + splitter.size();
907 i = start - 1;
908 }
909 }
910 }
911 if (in_quote)
912 throw Exception::ConversionError(__FILE__, __LINE__, OPENMS_PRETTY_FUNCTION,
913 "unbalanced quotation marks in string '" + s + "'");
914 substrings.push_back(s.substr(start));
915 return substrings.size() > 1;
916 }
917
919 template <class StringIterator>
920 inline void concatenate(std::string& target,
921 StringIterator first, StringIterator last,
922 const std::string& glue = "")
923 {
924 if (first == last) { target.clear(); return; }
925 target = *first;
926 for (auto it = ++first; it != last; ++it)
927 {
928 target += glue;
929 target += *it;
930 }
931 }
932
934 template <class StringIterator>
935 inline std::string concatenate(StringIterator first, StringIterator last,
936 const std::string& glue = "")
937 {
938 std::string result;
939 concatenate(result, first, last, glue);
940 return result;
941 }
942
954 template <std::ranges::range Container>
955 inline std::string concatenate(const Container& container, const std::string& glue = "")
956 {
957 return concatenate(container.begin(), container.end(), glue);
958 }
959
960 } // namespace StringUtils
961
962
963 // =========================================================================
964 // Legacy StringConversions namespace — thin forwarding layer
965 // Prefer StringUtils:: for new code.
966 // =========================================================================
967 namespace StringConversions
968 {
969 template <typename T>
970 inline void append(const T& i, std::string& target)
971 { StringUtils::appendToStr(i, target); }
972
973 inline void append(float f, std::string& target)
974 { StringUtils::appendToStr(f, target); }
975 inline void append(double d, std::string& target)
976 { StringUtils::appendToStr(d, target); }
977 inline void append(long double ld, std::string& target)
978 { StringUtils::appendToStr(ld, target); }
979 inline void appendLowP(float f, std::string& target)
980 { StringUtils::appendToStrLowP(f, target); }
981 inline void appendLowP(double d, std::string& target)
982 { StringUtils::appendToStrLowP(d, target); }
983 inline void appendLowP(long double ld, std::string& target)
984 { StringUtils::appendToStrLowP(ld, target); }
985 inline void append(const DataValue& d, bool full_precision, std::string& target)
986 { StringUtils::appendToStr(d, full_precision, target); }
987
988 template <typename T>
989 inline std::string toString(const T& i)
990 { return StringUtils::toStr(i); }
991 inline std::string toString(float f, bool fp = true)
992 { return StringUtils::toStr(f, fp); }
993 inline std::string toString(double d, bool fp = true)
994 { return StringUtils::toStr(d, fp); }
995 inline std::string toString(long double ld, bool fp = true)
996 { return StringUtils::toStr(ld, fp); }
997 inline std::string toString(const DataValue& d, bool fp = true)
998 { return StringUtils::toStr(d, fp); }
999 inline std::string toString()
1000 { return {}; }
1001
1002 inline std::string toStringLowP(float f) { return StringUtils::toStr(f, false); }
1003 inline std::string toStringLowP(double d) { return StringUtils::toStr(d, false); }
1004 inline std::string toStringLowP(long double ld) { return StringUtils::toStr(ld, false); }
1005
1006 } // namespace StringConversions
1007
1008} // namespace OpenMS
1009
1010
1011// =============================================================================
1012// Global operator+ / operator+= for std::string + numeric types
1013//
1014// Placing these in the global namespace ensures they are found by unqualified
1015// lookup regardless of which OpenMS namespace the caller is in.
1016// Using pass-by-value for operator+ enables move semantics for rvalue strings.
1017// =============================================================================
1018
1019inline std::string& operator+=(std::string& s, int i)
1020{ OpenMS::StringUtils::appendToStr(i, s); return s; }
1021inline std::string& operator+=(std::string& s, unsigned int i)
1022{ OpenMS::StringUtils::appendToStr(i, s); return s; }
1023inline std::string& operator+=(std::string& s, short int i)
1024{ OpenMS::StringUtils::appendToStr(i, s); return s; }
1025inline std::string& operator+=(std::string& s, short unsigned int i)
1026{ OpenMS::StringUtils::appendToStr(i, s); return s; }
1027inline std::string& operator+=(std::string& s, long int i)
1028{ OpenMS::StringUtils::appendToStr(i, s); return s; }
1029inline std::string& operator+=(std::string& s, long unsigned int i)
1030{ OpenMS::StringUtils::appendToStr(i, s); return s; }
1031inline std::string& operator+=(std::string& s, long long unsigned int i)
1032{ OpenMS::StringUtils::appendToStr(i, s); return s; }
1033inline std::string& operator+=(std::string& s, long long signed int i)
1034{ OpenMS::StringUtils::appendToStr(i, s); return s; }
1035inline std::string& operator+=(std::string& s, float f)
1036{ OpenMS::StringUtils::appendToStr(f, s); return s; }
1037inline std::string& operator+=(std::string& s, double d)
1038{ OpenMS::StringUtils::appendToStr(d, s); return s; }
1039inline std::string& operator+=(std::string& s, long double ld)
1040{ OpenMS::StringUtils::appendToStr(ld, s); return s; }
1041
1042// operator+ reuses operator+= on a value-copy, enabling move-from-rvalue
1043inline std::string operator+(std::string s, int i)
1044{ return std::move(s += i); }
1045inline std::string operator+(std::string s, unsigned int i)
1046{ return std::move(s += i); }
1047inline std::string operator+(std::string s, short int i)
1048{ return std::move(s += i); }
1049inline std::string operator+(std::string s, short unsigned int i)
1050{ return std::move(s += i); }
1051inline std::string operator+(std::string s, long int i)
1052{ return std::move(s += i); }
1053inline std::string operator+(std::string s, long unsigned int i)
1054{ return std::move(s += i); }
1055inline std::string operator+(std::string s, long long unsigned int i)
1056{ return std::move(s += i); }
1057inline std::string operator+(std::string s, long long signed int i)
1058{ return std::move(s += i); }
1059inline std::string operator+(std::string s, float f)
1060{ return std::move(s += f); }
1061inline std::string operator+(std::string s, double d)
1062{ return std::move(s += d); }
1063inline std::string operator+(std::string s, long double ld)
1064{ return std::move(s += ld); }
std::string & operator+=(std::string &s, int i)
Definition StringUtils.h:1019
std::string operator+(std::string s, int i)
Definition StringUtils.h:1043
Class to hold strings, numeric values, lists of strings and lists of numeric values.
Definition DataValue.h:32
Invalid conversion exception.
Definition Exception.h:331
Int overflow exception.
Definition Exception.h:211
Int underflow exception.
Definition Exception.h:175
Class to hold strings, numeric values, vectors of strings and vectors of numeric values using the stl...
Definition ParamValue.h:31
Karma full-precision float/double/long double policy with NaN→"NaN".
Definition StringUtils.h:56
static unsigned precision(T)
Definition StringUtils.h:59
static unsigned floatfield(T n)
Definition StringUtils.h:61
static bool nan(OutputIterator &sink, T n, bool force_sign)
Definition StringUtils.h:71
boost::spirit::karma::real_policies< T > base_policy_type
Definition StringUtils.h:57
Karma low-precision (≤3 fractional digits) policy with NaN→"NaN".
Definition StringUtils.h:82
static bool nan(OutputIterator &sink, T n, bool force_sign)
Definition StringUtils.h:86
boost::spirit::karma::real_policies< T > base_policy_type
Definition StringUtils.h:83
Definition StringUtils.h:116
static boost::spirit::qi::int_parser parse_int_
Definition StringUtils.h:170
static double toDouble(const std::string &s)
Parse double from string (leading/trailing whitespace allowed)
static bool extractDouble(IteratorT &begin, const IteratorT &end, double &target)
Definition StringUtils.h:130
static Int64 toInt64(const std::string &s)
Parse int64 from string (leading/trailing whitespace allowed)
static boost::spirit::qi::real_parser< double, real_policies_NANfixed_< double > > parse_double_
Definition StringUtils.h:168
static boost::spirit::qi::real_parser< float, real_policies_NANfixed_< float > > parse_float_
Definition StringUtils.h:169
static Int32 toInt32(const std::string &s)
Parse int32 from string (leading/trailing whitespace allowed)
static float toFloat(const std::string &s)
Parse float from string (leading/trailing whitespace allowed)
static bool extractInt(IteratorT &begin, const IteratorT &end, int &target)
Definition StringUtils.h:138
int32_t Int32
Signed integer type (32bit)
Definition Types.h:26
int64_t Int64
Signed integer type (64bit)
Definition Types.h:40
uint8_t Byte
Byte type.
Definition Types.h:81
int Int
Signed integer type.
Definition Types.h:72
unsigned int UInt
Unsigned integer type.
Definition Types.h:64
size_t Size
Size type e.g. used as variable which can hold result of size()
Definition Types.h:97
boost::spirit::karma::real_generator< float, BK_PrecPolicyFull< float > > FloatFullGen
Definition StringUtils.h:94
const DoubleShortGen doubleShort
Definition StringUtils.h:106
const FloatFullGen floatFull
Definition StringUtils.h:102
const FloatShortGen floatShort
Definition StringUtils.h:105
boost::spirit::karma::real_generator< long double, BK_PrecPolicyShort< long double > > LDShortGen
Definition StringUtils.h:100
boost::spirit::karma::real_generator< float, BK_PrecPolicyShort< float > > FloatShortGen
Definition StringUtils.h:98
const LDFullGen ldFull
Definition StringUtils.h:104
const DoubleFullGen doubleFull
Definition StringUtils.h:103
boost::spirit::karma::real_generator< long double, BK_PrecPolicyFull< long double > > LDFullGen
Definition StringUtils.h:96
boost::spirit::karma::real_generator< double, BK_PrecPolicyFull< double > > DoubleFullGen
Definition StringUtils.h:95
const LDShortGen ldShort
Definition StringUtils.h:107
boost::spirit::karma::real_generator< double, BK_PrecPolicyShort< double > > DoubleShortGen
Definition StringUtils.h:99
void append(const T &i, std::string &target)
Definition StringUtils.h:970
void appendLowP(float f, std::string &target)
Definition StringUtils.h:979
std::string toStringLowP(float f)
Definition StringUtils.h:1002
std::string toString()
Definition StringUtils.h:999
float toFloat(const std::string &s)
Definition StringUtils.h:338
std::string & fillRight(std::string &s, char c, UInt size)
Definition StringUtils.h:635
bool extractDouble(IteratorT &begin, const IteratorT &end, double &target)
Definition StringUtils.h:342
bool extractInt(IteratorT &begin, const IteratorT &end, int &target)
Definition StringUtils.h:346
Int64 toInt64(const std::string &s)
Definition StringUtils.h:337
std::string substituted(std::string s, char from, char to)
substitute on a copy (for chained/rvalue expressions)
Definition StringUtils.h:667
bool hasPrefix(const std::string &s, const std::string &prefix)
Definition StringUtils.h:438
std::string & toUpper(std::string &s)
Definition StringUtils.h:578
std::string chop(const std::string &s, Size n)
Remove n characters from the end; returns empty string if n >= size.
Definition StringUtils.h:549
void appendToStr(const T &i, std::string &target)
Append i to target (Boost.Karma, fast)
Definition StringUtils.h:186
std::string & trim(std::string &s)
Definition StringUtils.h:560
std::string & unquote(std::string &s, char q='"', QuotingMethod method = QuotingMethod::ESCAPE)
Definition StringUtils.h:734
bool hasSuffix(const std::string &s, const std::string &sfx)
Definition StringUtils.h:447
std::string & fillLeft(std::string &s, char c, UInt size)
Definition StringUtils.h:628
std::string & firstToUpper(std::string &s)
Definition StringUtils.h:594
void appendToStrLowP(float f, std::string &target)
Append float (low precision, 3 fractional digits) to target.
Definition StringUtils.h:217
std::string suffix(const std::string &s, size_t length)
Definition StringUtils.h:482
std::string substr(const std::string &s, size_t pos=0, size_t n=std::string::npos)
Wrapper around std::string::substr; clamps pos to [0, size].
Definition StringUtils.h:525
std::string random(UInt length)
Returns a random string of length characters from [0-9a-zA-Z].
Definition StringUtils.h:418
bool isQuoted(const std::string &s, char q)
Definition StringUtils.h:713
std::string toLowered(std::string s)
Returns a lower-cased copy of s (for use in chained/rvalue expressions)
Definition StringUtils.h:592
std::string & removeWhitespaces(std::string &s)
Definition StringUtils.h:688
std::string numberLength(double d, UInt n)
Returns a string for d with at most n characters total (scientific notation if needed)
Definition StringUtils.h:388
std::string number(double d, UInt n)
Returns a string with exactly n decimal places for d.
Definition StringUtils.h:380
std::string prefix(const std::string &s, size_t length)
Definition StringUtils.h:475
const char * skipNonWhitespace(const char *p, const char *p_end)
Returns pointer to first whitespace character in [p, p_end), or p_end.
bool hasSubstring(const std::string &s, const std::string &sub)
Definition StringUtils.h:456
const char * skipWhitespace(const char *p, const char *p_end)
Returns pointer to first non-whitespace character in [p, p_end), or p_end.
std::string trimmed(std::string s)
Returns a trimmed copy of s (for use in chained/rvalue expressions)
Definition StringUtils.h:576
std::string toStr(int i)
Definition StringUtils.h:257
bool split(const std::string &s, char splitter, std::vector< std::string > &substrings, bool quote_protect=false)
Definition StringUtils.h:761
std::string & ensureLastChar(std::string &s, char end)
Definition StringUtils.h:680
void concatenate(std::string &target, StringIterator first, StringIterator last, const std::string &glue="")
Join elements [first, last) with glue between them, storing result in target.
Definition StringUtils.h:920
std::string & simplify(std::string &s)
Definition StringUtils.h:606
std::string & reverse(std::string &s)
Definition StringUtils.h:600
bool has(const std::string &s, Byte byte)
Definition StringUtils.h:463
double toDouble(const std::string &s)
Definition StringUtils.h:339
std::string & toLower(std::string &s)
Definition StringUtils.h:584
std::string & remove(std::string &s, char what)
Definition StringUtils.h:670
std::string toUppered(std::string s)
Returns an upper-cased copy of s (for use in chained/rvalue expressions)
Definition StringUtils.h:590
Int32 toInt32(const std::string &s)
Definition StringUtils.h:336
bool split_quoted(const std::string &s, const std::string &splitter, std::vector< std::string > &substrings, char q='"', QuotingMethod method = QuotingMethod::ESCAPE)
Definition StringUtils.h:857
std::string & quote(std::string &s, char q='"', QuotingMethod method = QuotingMethod::ESCAPE)
Definition StringUtils.h:718
std::string & substitute(std::string &s, char from, char to)
Definition StringUtils.h:642
Main OpenMS namespace.
Definition openswathalgo/include/OpenMS/OPENSWATHALGO/DATAACCESS/ISpectrumAccess.h:19
QuotingMethod
How to handle embedded quotes when quoting strings.
Definition StringUtils.h:43
constexpr Int writtenDigits(const FloatingPointType &=FloatingPointType())
Number of digits commonly used for writing a floating point type (a.k.a. precision)....
Definition Types.h:264
static bool parse_nan(Iterator &first, Iterator const &last, Attribute &attr_)
Definition StringUtils.h:148