Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file StringUtils.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Laura Bieker
17 : /// @author Michael Behrisch
18 : /// @author Robert Hilbrich
19 : /// @date unknown
20 : ///
21 : // Some static methods for string processing
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <string>
26 : #include <iostream>
27 : #include <cstdio>
28 : #include <cstring>
29 : #include <regex>
30 : #ifdef WIN32
31 : #define NOMINMAX
32 : #include <windows.h>
33 : #undef NOMINMAX
34 : #else
35 : #include <unistd.h>
36 : #endif
37 : #include <utils/common/UtilExceptions.h>
38 : #include <utils/common/ToString.h>
39 : #include <utils/common/StringTokenizer.h>
40 : #include "StringUtils.h"
41 :
42 : #define KM_PER_MILE 1.609344
43 :
44 :
45 : // ===========================================================================
46 : // static member definitions
47 : // ===========================================================================
48 : std::string StringUtils::emptyString;
49 :
50 :
51 : // ===========================================================================
52 : // method definitions
53 : // ===========================================================================
54 : std::string
55 1584660 : StringUtils::prune(const std::string& str) {
56 : const std::string::size_type endpos = str.find_last_not_of(" \t\n\r");
57 1584660 : if (std::string::npos != endpos) {
58 1581523 : const int startpos = (int)str.find_first_not_of(" \t\n\r");
59 1581523 : return str.substr(startpos, endpos - startpos + 1);
60 : }
61 3137 : return "";
62 : }
63 :
64 :
65 : std::string
66 59181 : StringUtils::pruneZeros(const std::string& str, int max) {
67 : const std::string::size_type endpos = str.find_last_not_of("0");
68 59181 : if (endpos != std::string::npos && str.back() == '0') {
69 40040 : std::string res = str.substr(0, MAX2((int)str.size() - max, (int)endpos + 1));
70 40040 : return res;
71 : }
72 : return str;
73 : }
74 :
75 : std::string
76 8232274 : StringUtils::to_lower_case(const std::string& str) {
77 : std::string s = str;
78 : std::transform(s.begin(), s.end(), s.begin(), [](char c) {
79 56939369 : return (char)::tolower(c);
80 : });
81 8232274 : return s;
82 : }
83 :
84 :
85 : std::string
86 0 : StringUtils::to_upper_case(const std::string& str) {
87 : std::string s = str;
88 : std::transform(s.begin(), s.end(), s.begin(), [](char c) {
89 0 : return (char)::toupper(c);
90 : });
91 0 : return s;
92 : }
93 :
94 :
95 : std::string
96 1956 : StringUtils::latin1_to_utf8(std::string str) {
97 : // inspired by http://stackoverflow.com/questions/4059775/convert-iso-8859-1-strings-to-utf-8-in-c-c
98 : std::string result;
99 8781 : for (const auto& c : str) {
100 6825 : const unsigned char uc = (unsigned char)c;
101 6825 : if (uc < 128) {
102 : result += uc;
103 : } else {
104 100 : result += (char)(0xc2 + (uc > 0xbf));
105 100 : result += (char)((uc & 0x3f) + 0x80);
106 : }
107 : }
108 1956 : return result;
109 : }
110 :
111 :
112 : std::string
113 1294602 : StringUtils::convertUmlaute(std::string str) {
114 3883806 : str = replace(str, "\xE4", "ae");
115 3883806 : str = replace(str, "\xC4", "Ae");
116 3883806 : str = replace(str, "\xF6", "oe");
117 3883806 : str = replace(str, "\xD6", "Oe");
118 3883806 : str = replace(str, "\xFC", "ue");
119 3883806 : str = replace(str, "\xDC", "Ue");
120 3883806 : str = replace(str, "\xDF", "ss");
121 3883806 : str = replace(str, "\xC9", "E");
122 3883806 : str = replace(str, "\xE9", "e");
123 3883806 : str = replace(str, "\xC8", "E");
124 3883806 : str = replace(str, "\xE8", "e");
125 1294602 : return str;
126 : }
127 :
128 :
129 : std::string
130 71047673 : StringUtils::replace(std::string str, const std::string& what, const std::string& by) {
131 : std::string::size_type idx = str.find(what);
132 71047673 : const int what_len = (int)what.length();
133 71047673 : if (what_len > 0) {
134 71047672 : const int by_len = (int)by.length();
135 71051545 : while (idx != std::string::npos) {
136 3873 : str = str.replace(idx, what_len, by);
137 3873 : idx = str.find(what, idx + by_len);
138 : }
139 : }
140 71047673 : return str;
141 : }
142 :
143 :
144 : std::string
145 1329411 : StringUtils::substituteEnvironment(const std::string& str, const std::chrono::time_point<std::chrono::system_clock>* const timeRef) {
146 : std::string s = str;
147 1329411 : if (timeRef != nullptr) {
148 : const std::string::size_type localTimeIndex = str.find("${LOCALTIME}");
149 : const std::string::size_type utcIndex = str.find("${UTC}");
150 : const bool isUTC = utcIndex != std::string::npos;
151 1329405 : if (localTimeIndex != std::string::npos || isUTC) {
152 6 : const time_t rawtime = std::chrono::system_clock::to_time_t(*timeRef);
153 : char buffer [80];
154 6 : struct tm* timeinfo = isUTC ? gmtime(&rawtime) : localtime(&rawtime);
155 6 : strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S.", timeinfo);
156 : auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(*timeRef);
157 : auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(*timeRef - seconds);
158 6 : const std::string micro = buffer + toString(microseconds.count());
159 6 : if (isUTC) {
160 : s.replace(utcIndex, 6, micro);
161 : } else {
162 : s.replace(localTimeIndex, 12, micro);
163 : }
164 : }
165 : }
166 : const std::string::size_type pidIndex = str.find("${PID}");
167 1329411 : if (pidIndex != std::string::npos) {
168 : #ifdef WIN32
169 : s.replace(pidIndex, 6, toString(::GetCurrentProcessId()));
170 : #else
171 6 : s.replace(pidIndex, 6, toString(::getpid()));
172 : #endif
173 : }
174 1329411 : if (std::getenv("SUMO_LOGO") == nullptr) {
175 3988233 : s = replace(s, "${SUMO_LOGO}", "${SUMO_HOME}/data/logo/sumo-128x138.png");
176 : }
177 : const std::string::size_type tildeIndex = str.find("~");
178 1329411 : if (tildeIndex == 0) {
179 3 : s.replace(0, 1, "${HOME}");
180 : }
181 3988233 : s = replace(s, ",~", ",${HOME}");
182 : #ifdef WIN32
183 : if (std::getenv("HOME") == nullptr) {
184 : s = replace(s, "${HOME}", "${USERPROFILE}");
185 : }
186 : #endif
187 :
188 : // Expression for an environment variables, e.g. ${NAME}
189 : // Note: - R"(...)" is a raw string literal syntax to simplify a regex declaration
190 : // - .+? looks for the shortest match (non-greedy)
191 : // - (.+?) defines a "subgroup" which is already stripped of the $ and {, }
192 1329411 : std::regex envVarExpr(R"(\$\{(.+?)\})");
193 :
194 : // Are there any variables in this string?
195 : std::smatch match;
196 : std::string strIter = s;
197 :
198 : // Loop over the entire value string and look for variable names
199 1329477 : while (std::regex_search(strIter, match, envVarExpr)) {
200 : std::string varName = match[1];
201 :
202 : // Find the variable in the environment and its value
203 : std::string varValue;
204 66 : if (std::getenv(varName.c_str()) != nullptr) {
205 65 : varValue = std::getenv(varName.c_str());
206 : }
207 :
208 : // Replace the variable placeholder with its value in the original string
209 264 : s = std::regex_replace(s, std::regex("\\$\\{" + varName + "\\}"), varValue);
210 :
211 : // Continue the loop with the remainder of the string
212 132 : strIter = match.suffix();
213 : }
214 1329411 : return s;
215 1329411 : }
216 :
217 :
218 : std::string
219 87865 : StringUtils::isoTimeString(const std::chrono::time_point<std::chrono::system_clock>* const timeRef) {
220 87865 : const std::chrono::system_clock::time_point now = timeRef == nullptr ? std::chrono::system_clock::now() : *timeRef;
221 : const auto now_seconds = std::chrono::time_point_cast<std::chrono::seconds>(now);
222 87865 : const std::time_t now_c = std::chrono::system_clock::to_time_t(now);
223 : const auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - now_seconds).count();
224 87865 : std::tm local_tm = *std::localtime(&now_c);
225 :
226 : // Get the time zone offset
227 87865 : std::time_t utc_time = std::time(nullptr);
228 87865 : std::tm utc_tm = *std::gmtime(&utc_time);
229 87865 : const double offset = std::difftime(std::mktime(&local_tm), std::mktime(&utc_tm)) / 3600.0;
230 87865 : const int hours_offset = static_cast<int>(offset);
231 87865 : const int minutes_offset = static_cast<int>((offset - hours_offset) * 60);
232 :
233 : // Format the time
234 87865 : std::ostringstream oss;
235 : char buf[32];
236 87865 : std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &local_tm);
237 : oss << buf << "."
238 : << std::setw(6) << std::setfill('0') << std::abs(microseconds)
239 : << (hours_offset >= 0 ? "+" : "-")
240 87865 : << std::setw(2) << std::setfill('0') << std::abs(hours_offset) << ":"
241 175730 : << std::setw(2) << std::setfill('0') << std::abs(minutes_offset);
242 87865 : return oss.str();
243 87865 : }
244 :
245 :
246 : bool
247 6704941 : StringUtils::startsWith(const std::string& str, const std::string prefix) {
248 6704941 : return str.compare(0, prefix.length(), prefix) == 0;
249 : }
250 :
251 :
252 : bool
253 224327 : StringUtils::endsWith(const std::string& str, const std::string suffix) {
254 224327 : if (str.length() >= suffix.length()) {
255 224304 : return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
256 : } else {
257 : return false;
258 : }
259 : }
260 :
261 :
262 : std::string
263 0 : StringUtils::padFront(const std::string& str, int length, char padding) {
264 0 : return std::string(MAX2(0, length - (int)str.size()), padding) + str;
265 : }
266 :
267 :
268 : std::string
269 1476736 : StringUtils::escapeXML(const std::string& orig, const bool maskDoubleHyphen) {
270 4430208 : std::string result = replace(orig, "&", "&");
271 4430208 : result = replace(result, ">", ">");
272 4430208 : result = replace(result, "<", "<");
273 4430208 : result = replace(result, "\"", """);
274 1476736 : if (maskDoubleHyphen) {
275 2321463 : result = replace(result, "--", "--");
276 : }
277 47255552 : for (char invalid = '\1'; invalid < ' '; invalid++) {
278 228894080 : result = replace(result, std::string(1, invalid).c_str(), "");
279 : }
280 4430208 : return replace(result, "'", "'");
281 : }
282 :
283 :
284 : std::string
285 0 : StringUtils::escapeShell(const std::string& orig) {
286 0 : std::string result = replace(orig, "\"", "\\\"");
287 0 : return result;
288 : }
289 :
290 :
291 : std::string
292 5121 : StringUtils::urlEncode(const std::string& toEncode, const std::string encodeWhich) {
293 5121 : std::ostringstream out;
294 :
295 131266 : for (int i = 0; i < (int)toEncode.length(); ++i) {
296 126145 : const char t = toEncode.at(i);
297 :
298 126145 : if ((encodeWhich != "" && encodeWhich.find(t) == std::string::npos) ||
299 1 : (encodeWhich == "" &&
300 0 : ((t >= 45 && t <= 57) || // hyphen, period, slash, 0-9
301 0 : (t >= 65 && t <= 90) || // A-Z
302 : t == 95 || // underscore
303 : (t >= 97 && t <= 122) || // a-z
304 : t == 126)) // tilde
305 : ) {
306 126144 : out << toEncode.at(i);
307 : } else {
308 2 : out << charToHex(toEncode.at(i));
309 : }
310 : }
311 :
312 5121 : return out.str();
313 5121 : }
314 :
315 :
316 : std::string
317 92710 : StringUtils::urlDecode(const std::string& toDecode) {
318 92710 : std::ostringstream out;
319 :
320 1662439 : for (int i = 0; i < (int)toDecode.length(); ++i) {
321 1569731 : if (toDecode.at(i) == '%') {
322 2 : std::string str(toDecode.substr(i + 1, 2));
323 2 : out << hexToChar(str);
324 0 : i += 2;
325 : } else {
326 1569729 : out << toDecode.at(i);
327 : }
328 : }
329 :
330 92708 : return out.str();
331 92710 : }
332 :
333 : std::string
334 1 : StringUtils::charToHex(unsigned char c) {
335 : short i = c;
336 :
337 1 : std::stringstream s;
338 :
339 1 : s << "%" << std::setw(2) << std::setfill('0') << std::hex << i;
340 :
341 1 : return s.str();
342 1 : }
343 :
344 :
345 : unsigned char
346 2 : StringUtils::hexToChar(const std::string& str) {
347 2 : short c = 0;
348 2 : if (!str.empty()) {
349 2 : std::istringstream in(str);
350 2 : in >> std::hex >> c;
351 2 : if (in.fail()) {
352 4 : throw NumberFormatException(str + " could not be interpreted as hex");
353 : }
354 2 : }
355 0 : return static_cast<unsigned char>(c);
356 : }
357 :
358 :
359 : int
360 18821112 : StringUtils::toInt(const std::string& sData) {
361 18821112 : long long int result = toLong(sData);
362 18803645 : if (result > std::numeric_limits<int>::max() || result < std::numeric_limits<int>::min()) {
363 3 : throw NumberFormatException(toString(result) + " int overflow");
364 : }
365 18803644 : return (int)result;
366 : }
367 :
368 :
369 : bool
370 8 : StringUtils::isInt(const std::string& sData) {
371 : // first check if can be converted to long int
372 8 : if (isLong(sData)) {
373 0 : const long long int result = toLong(sData);
374 : // now check if the result is in the range of an int
375 0 : return ((result <= std::numeric_limits<int>::max()) && (result >= std::numeric_limits<int>::min()));
376 : }
377 : return false;
378 : }
379 :
380 :
381 : int
382 0 : StringUtils::toIntSecure(const std::string& sData, int def) {
383 0 : if (sData.length() == 0) {
384 : return def;
385 : }
386 0 : return toInt(sData);
387 : }
388 :
389 :
390 : long long int
391 20260155 : StringUtils::toLong(const std::string& sData) {
392 : const char* const data = sData.c_str();
393 20260155 : if (data == 0 || data[0] == 0) {
394 64 : throw EmptyData();
395 : }
396 : char* end;
397 20260091 : errno = 0;
398 : #ifdef _MSC_VER
399 : long long int ret = _strtoi64(data, &end, 10);
400 : #else
401 20260091 : long long int ret = strtoll(data, &end, 10);
402 : #endif
403 20260091 : if (errno == ERANGE) {
404 0 : errno = 0;
405 0 : throw NumberFormatException("(long long integer range) " + sData);
406 : }
407 20260091 : if ((int)(end - data) != (int)strlen(data)) {
408 36052 : throw NumberFormatException("(long long integer format) " + sData);
409 : }
410 20242065 : return ret;
411 : }
412 :
413 :
414 : bool
415 8 : StringUtils::isLong(const std::string& sData) {
416 : const char* const data = sData.c_str();
417 8 : if (data == 0 || data[0] == 0) {
418 : return false;
419 : }
420 : char* end;
421 : // reset errno before parsing, to keep errors
422 8 : errno = 0;
423 : // continue depending of current plattform
424 : #ifdef _MSC_VER
425 : _strtoi64(data, &end, 10);
426 : #else
427 8 : strtoll(data, &end, 10);
428 : #endif
429 : // check out of range
430 8 : if (errno == ERANGE) {
431 : return false;
432 : }
433 : // check length of converted data
434 8 : if ((int)(end - data) != (int)strlen(data)) {
435 : return false;
436 : }
437 : return true;
438 : }
439 :
440 :
441 : int
442 962 : StringUtils::hexToInt(const std::string& sData) {
443 962 : if (sData.length() == 0) {
444 0 : throw EmptyData();
445 : }
446 962 : size_t idx = 0;
447 : int result;
448 : try {
449 962 : if (sData[0] == '#') { // for html color codes
450 878 : result = std::stoi(sData.substr(1), &idx, 16);
451 878 : idx++;
452 : } else {
453 : result = std::stoi(sData, &idx, 16);
454 : }
455 0 : } catch (...) {
456 0 : throw NumberFormatException("(hex integer format) " + sData);
457 0 : }
458 962 : if (idx != sData.length()) {
459 0 : throw NumberFormatException("(hex integer format) " + sData);
460 : }
461 962 : return result;
462 : }
463 :
464 :
465 : bool
466 0 : StringUtils::isHex(std::string sData) {
467 0 : if (sData.length() == 0) {
468 : return false;
469 : }
470 : // remove the first character (for HTML color codes)
471 0 : if (sData[0] == '#') {
472 0 : sData = sData.substr(1);
473 : }
474 : const char* sDataPtr = sData.c_str();
475 : char* returnPtr;
476 : // reset errno
477 0 : errno = 0;
478 : // call string to long (size 16) from standard library
479 0 : strtol(sDataPtr, &returnPtr, 16);
480 : // check out of range
481 0 : if (errno == ERANGE) {
482 : return false;
483 : }
484 : // check if there was an error converting sDataPtr to double,
485 0 : if (sDataPtr == returnPtr) {
486 : return false;
487 : }
488 : // compare size of start and end points
489 0 : if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
490 : return false;
491 : }
492 : return true;
493 : }
494 :
495 :
496 : double
497 75498932 : StringUtils::toDouble(const std::string& sData) {
498 75498932 : if (sData.size() == 0) {
499 191 : throw EmptyData();
500 : }
501 : try {
502 75498741 : size_t idx = 0;
503 : const double result = std::stod(sData, &idx);
504 75497160 : if (idx != sData.size()) {
505 34 : throw NumberFormatException("(double format) " + sData);
506 : } else {
507 75497143 : return result;
508 : }
509 1598 : } catch (...) {
510 : // invalid_argument or out_of_range
511 3196 : throw NumberFormatException("(double) " + sData);
512 1598 : }
513 : }
514 :
515 :
516 : bool
517 0 : StringUtils::isDouble(const std::string& sData) {
518 0 : if (sData.size() == 0) {
519 : return false;
520 : }
521 : const char* sDataPtr = sData.c_str();
522 : char* returnPtr;
523 : // reset errno
524 0 : errno = 0;
525 : // call string to double from standard library
526 0 : strtod(sDataPtr, &returnPtr);
527 : // check out of range
528 0 : if (errno == ERANGE) {
529 : return false;
530 : }
531 : // check if there was an error converting sDataPtr to double,
532 0 : if (sDataPtr == returnPtr) {
533 : return false;
534 : }
535 : // compare size of start and end points
536 0 : if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
537 : return false;
538 : }
539 : return true;
540 : }
541 :
542 :
543 : double
544 352 : StringUtils::toDoubleSecure(const std::string& sData, const double def) {
545 352 : if (sData.length() == 0) {
546 : return def;
547 : }
548 352 : return toDouble(sData);
549 : }
550 :
551 :
552 : bool
553 3320808 : StringUtils::toBool(const std::string& sData) {
554 3320808 : if (sData.length() == 0) {
555 1 : throw EmptyData();
556 : }
557 3320807 : const std::string s = to_lower_case(sData);
558 3320807 : if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
559 : return true;
560 : }
561 2684537 : if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
562 : return false;
563 : }
564 147 : throw BoolFormatException(s);
565 : }
566 :
567 :
568 : bool
569 75610 : StringUtils::isBool(const std::string& sData) {
570 75610 : if (sData.length() == 0) {
571 : return false;
572 : }
573 17622 : const std::string s = to_lower_case(sData);
574 : // check true values
575 17622 : if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
576 : return true;
577 : }
578 : // check false values
579 1058 : if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
580 : return true;
581 : }
582 : // no valid true or false values
583 : return false;
584 : }
585 :
586 :
587 : MMVersion
588 47571 : StringUtils::toVersion(const std::string& sData) {
589 142713 : std::vector<std::string> parts = StringTokenizer(sData, ".").getVector();
590 95142 : return MMVersion(toInt(parts.front()), toDouble(parts.back()));
591 47571 : }
592 :
593 :
594 : double
595 2205 : StringUtils::parseDist(const std::string& sData) {
596 2205 : if (sData.size() == 0) {
597 1 : throw EmptyData();
598 : }
599 : try {
600 2204 : size_t idx = 0;
601 : const double result = std::stod(sData, &idx);
602 2203 : if (idx != sData.size()) {
603 4 : const std::string unit = prune(sData.substr(idx));
604 4 : if (unit == "m" || unit == "metre" || unit == "meter" || unit == "metres" || unit == "meters") {
605 : return result;
606 : }
607 2 : if (unit == "km" || unit == "kilometre" || unit == "kilometer" || unit == "kilometres" || unit == "kilometers") {
608 1 : return result * 1000.;
609 : }
610 1 : if (unit == "mi" || unit == "mile" || unit == "miles") {
611 0 : return result * 1000. * KM_PER_MILE;
612 : }
613 1 : if (unit == "nmi") {
614 0 : return result * 1852.;
615 : }
616 1 : if (unit == "ft" || unit == "foot" || unit == "feet") {
617 0 : return result * 12. * 0.0254;
618 : }
619 1 : if (unit == "\"" || unit == "in" || unit == "inch" || unit == "inches") {
620 0 : return result * 0.0254;
621 : }
622 1 : if (unit[0] == '\'') {
623 0 : double inches = 12 * result;
624 0 : if (unit.length() > 1) {
625 0 : inches += std::stod(unit.substr(1), &idx);
626 1 : if (unit.substr(idx) == "\"") {
627 0 : return inches * 0.0254;
628 : }
629 : }
630 : }
631 2 : throw NumberFormatException("(distance format) " + sData);
632 : } else {
633 : return result;
634 : }
635 2 : } catch (...) {
636 : // invalid_argument or out_of_range
637 4 : throw NumberFormatException("(double) " + sData);
638 2 : }
639 : }
640 :
641 :
642 : double
643 8125 : StringUtils::parseSpeed(const std::string& sData, const bool defaultKmph) {
644 8125 : if (sData.size() == 0) {
645 1 : throw EmptyData();
646 : }
647 : try {
648 8124 : size_t idx = 0;
649 : const double result = std::stod(sData, &idx);
650 8120 : if (idx != sData.size()) {
651 1245 : const std::string unit = prune(sData.substr(idx));
652 1245 : if (unit == "km/h" || unit == "kph" || unit == "kmh" || unit == "kmph") {
653 3 : return result / 3.6;
654 : }
655 1242 : if (unit == "m/s") {
656 : return result;
657 : }
658 1240 : if (unit == "mph") {
659 1239 : return result * KM_PER_MILE / 3.6;
660 : }
661 1 : if (unit == "knots") {
662 0 : return result * 1.852 / 3.6;
663 : }
664 2 : throw NumberFormatException("(speed format) " + sData);
665 : } else {
666 6875 : return defaultKmph ? result / 3.6 : result;
667 : }
668 5 : } catch (...) {
669 : // invalid_argument or out_of_range
670 10 : throw NumberFormatException("(double) " + sData);
671 5 : }
672 : }
673 :
674 :
675 :
676 : std::string
677 0 : StringUtils::trim_left(const std::string s, const std::string& t) {
678 : std::string result = s;
679 0 : result.erase(0, s.find_first_not_of(t));
680 0 : return result;
681 : }
682 :
683 : std::string
684 0 : StringUtils::trim_right(const std::string s, const std::string& t) {
685 : std::string result = s;
686 0 : result.erase(s.find_last_not_of(t) + 1);
687 0 : return result;
688 : }
689 :
690 : std::string
691 0 : StringUtils::trim(const std::string s, const std::string& t) {
692 0 : return trim_right(trim_left(s, t), t);
693 : }
694 :
695 :
696 : std::string
697 0 : StringUtils::wrapText(const std::string s, int width) {
698 0 : std::vector<std::string> parts = StringTokenizer(s).getVector();
699 : std::string result;
700 : std::string line;
701 : bool firstLine = true;
702 : bool firstWord = true;
703 0 : for (std::string p : parts) {
704 0 : if ((int)(line.size() + p.size()) < width || firstWord) {
705 0 : if (firstWord) {
706 : firstWord = false;
707 : } else {
708 : line += " ";
709 : }
710 : line += p;
711 : } else {
712 0 : if (firstLine) {
713 : firstLine = false;
714 : } else {
715 : result += "\n";
716 : }
717 : result += line;
718 : line.clear();
719 : line += p;
720 : }
721 : }
722 0 : if (line.size() > 0) {
723 0 : if (firstLine) {
724 : firstLine = false;
725 : } else {
726 : result += "\n";
727 : }
728 : result += line;
729 : }
730 0 : return result;
731 0 : }
732 :
733 :
734 : std::string
735 965 : StringUtils::adjustDecimalValue(double value, int precision) {
736 : // obtain value in string format with 20 decimals precision
737 965 : auto valueStr = toString(value, precision);
738 : // now clear all zeros
739 16969 : while (valueStr.size() > 1) {
740 16969 : if (valueStr.back() == '0') {
741 : valueStr.pop_back();
742 965 : } else if (valueStr.back() == '.') {
743 : valueStr.pop_back();
744 : return valueStr;
745 : } else {
746 : return valueStr;
747 : }
748 : }
749 : return valueStr;
750 : }
751 :
752 :
753 : /****************************************************************************/
|