musrfit 1.10.0
PStringUtils.cpp
Go to the documentation of this file.
1/***************************************************************************
2
3 PStringUtils.cpp
4
5 Author: Andreas Suter
6 e-mail: andreas.suter@psi.ch
7
8***************************************************************************/
9
10/***************************************************************************
11 * Copyright (C) 2007-2026 by Andreas Suter *
12 * andreas.suter@psi.ch *
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 * This program is distributed in the hope that it will be useful, *
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22 * GNU General Public License for more details. *
23 * *
24 * You should have received a copy of the GNU General Public License *
25 * along with this program; if not, write to the *
26 * Free Software Foundation, Inc., *
27 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
28 ***************************************************************************/
29
30#include <cctype>
31#include <cerrno>
32#include <charconv>
33#include <cstdlib>
34
35#include "PStringUtils.h"
36
37//--------------------------------------------------------------------------
38// Split (static)
39//--------------------------------------------------------------------------
48std::vector<std::string> PStringUtils::Split(const std::string &str, const std::string &delimiters)
49{
50 std::vector<std::string> tokens;
51 std::string::size_type start = str.find_first_not_of(delimiters);
52 while (start != std::string::npos) {
53 std::string::size_type end = str.find_first_of(delimiters, start);
54 if (end == std::string::npos) {
55 tokens.push_back(str.substr(start));
56 break;
57 }
58 tokens.push_back(str.substr(start, end - start));
59 start = str.find_first_not_of(delimiters, end);
60 }
61 return tokens;
62}
63
64//--------------------------------------------------------------------------
65// IsInt (static)
66//--------------------------------------------------------------------------
77bool PStringUtils::IsInt(const std::string &str)
78{
79 // all characters must be digits or whitespace, with an optional single
80 // leading sign (+/-) preceding the digits, and there must be at least one
81 // digit (surrounding/embedded whitespace is tolerated, e.g. for tokens
82 // split on ',' or ';' only).
83 bool hasDigit = false;
84 bool hasSign = false;
85 for (char c : str) {
86 if (std::isdigit(static_cast<unsigned char>(c))) {
87 hasDigit = true;
88 } else if (c == '+' || c == '-') {
89 // a sign is only valid before any digit and may appear at most once
90 if (hasDigit || hasSign)
91 return false;
92 hasSign = true;
93 } else if (!std::isspace(static_cast<unsigned char>(c))) {
94 return false;
95 }
96 }
97 return hasDigit;
98}
99
100//--------------------------------------------------------------------------
101// IsFloat (static)
102//--------------------------------------------------------------------------
111bool PStringUtils::IsFloat(const std::string &str)
112{
113 // mirror TString::IsFloat(): surrounding whitespace is ignored (e.g. for
114 // tokens split on ',' or ';' only), then a complete number is required.
115 const std::string ws(" \t\n\r\f\v");
116 std::string::size_type b = str.find_first_not_of(ws);
117 if (b == std::string::npos)
118 return false;
119 std::string::size_type e = str.find_last_not_of(ws);
120 const std::string t = str.substr(b, e - b + 1);
121
122 std::string::size_type i = 0;
123 if (t[i] == '+' || t[i] == '-')
124 ++i;
125 // reject things like "inf"/"nan" which strtod would otherwise accept
126 if (i >= t.size() || !(std::isdigit(static_cast<unsigned char>(t[i])) || t[i] == '.'))
127 return false;
128 const char *begin = t.c_str();
129 char *end = nullptr;
130 std::strtod(begin, &end);
131 return end == begin + t.size();
132}
133
134//--------------------------------------------------------------------------
135// ToInt (static)
136//--------------------------------------------------------------------------
152int PStringUtils::ToInt(const std::string &str, bool *ok)
153{
154 // mirror TString::Atoi(): skip leading whitespace, then parse the leading
155 // integer (a possible sign followed by decimal digits).
156 const char *begin = str.c_str();
157 const char *end = begin + str.size();
158 while (begin != end && std::isspace(static_cast<unsigned char>(*begin)))
159 ++begin;
160
161 int value = 0;
162 const std::from_chars_result res = std::from_chars(begin, end, value);
163 if (ok != nullptr)
164 *ok = (res.ec == std::errc{});
165
166 return value;
167}
168
169//--------------------------------------------------------------------------
170// ToDouble (static)
171//--------------------------------------------------------------------------
189double PStringUtils::ToDouble(const std::string &str, bool *ok)
190{
191 const char *begin = str.c_str();
192 char *end = nullptr;
193 errno = 0;
194 const double value = std::strtod(begin, &end);
195 if (ok != nullptr)
196 *ok = (end != begin) && (errno != ERANGE);
197
198 return value;
199}
200
201//--------------------------------------------------------------------------
202// IsEqualNoCase (static)
203//--------------------------------------------------------------------------
212bool PStringUtils::IsEqualNoCase(const std::string &a, const std::string &b)
213{
214 if (a.size() != b.size())
215 return false;
216 for (std::string::size_type i = 0; i < a.size(); ++i) {
217 if (std::tolower(static_cast<unsigned char>(a[i])) !=
218 std::tolower(static_cast<unsigned char>(b[i])))
219 return false;
220 }
221 return true;
222}
223
224//--------------------------------------------------------------------------
225// ContainsNoCase (static)
226//--------------------------------------------------------------------------
235bool PStringUtils::ContainsNoCase(const std::string &haystack, const std::string &needle)
236{
237 if (needle.empty())
238 return true;
239 if (needle.size() > haystack.size())
240 return false;
241 auto toLower = [](unsigned char c) { return std::tolower(c); };
242 for (std::string::size_type i = 0; i + needle.size() <= haystack.size(); ++i) {
243 std::string::size_type j = 0;
244 for (; j < needle.size(); ++j) {
245 if (toLower(haystack[i+j]) != toLower(needle[j]))
246 break;
247 }
248 if (j == needle.size())
249 return true;
250 }
251 return false;
252}
253
254//--------------------------------------------------------------------------
255// BeginsWithNoCase (static)
256//--------------------------------------------------------------------------
265bool PStringUtils::BeginsWithNoCase(const std::string &str, const std::string &prefix)
266{
267 if (prefix.size() > str.size())
268 return false;
269 for (std::string::size_type i = 0; i < prefix.size(); ++i) {
270 if (std::tolower(static_cast<unsigned char>(str[i])) !=
271 std::tolower(static_cast<unsigned char>(prefix[i])))
272 return false;
273 }
274 return true;
275}
static bool IsFloat(const std::string &str)
static bool ContainsNoCase(const std::string &haystack, const std::string &needle)
static int ToInt(const std::string &str, bool *ok=nullptr)
static bool BeginsWithNoCase(const std::string &str, const std::string &prefix)
static bool IsEqualNoCase(const std::string &a, const std::string &b)
static std::vector< std::string > Split(const std::string &str, const std::string &delimiters)
static double ToDouble(const std::string &str, bool *ok=nullptr)
static bool IsInt(const std::string &str)