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 1589779 : StringUtils::prune(const std::string& str) {
56 : const std::string::size_type endpos = str.find_last_not_of(" \t\n\r");
57 1589779 : if (std::string::npos != endpos) {
58 1586641 : const int startpos = (int)str.find_first_not_of(" \t\n\r");
59 1586641 : return str.substr(startpos, endpos - startpos + 1);
60 : }
61 3138 : return "";
62 : }
63 :
64 :
65 : std::string
66 57154 : StringUtils::pruneZeros(const std::string& str, int max) {
67 : const std::string::size_type endpos = str.find_last_not_of("0");
68 57154 : if (endpos != std::string::npos && str.back() == '0') {
69 38117 : std::string res = str.substr(0, MAX2((int)str.size() - max, (int)endpos + 1));
70 38117 : return res;
71 : }
72 : return str;
73 : }
74 :
75 : std::string
76 8287196 : 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 57285894 : return (char)::tolower(c);
80 : });
81 8287196 : 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 1314608 : StringUtils::convertUmlaute(std::string str) {
114 3943824 : str = replace(str, "\xE4", "ae");
115 3943824 : str = replace(str, "\xC4", "Ae");
116 3943824 : str = replace(str, "\xF6", "oe");
117 3943824 : str = replace(str, "\xD6", "Oe");
118 3943824 : str = replace(str, "\xFC", "ue");
119 3943824 : str = replace(str, "\xDC", "Ue");
120 3943824 : str = replace(str, "\xDF", "ss");
121 3943824 : str = replace(str, "\xC9", "E");
122 3943824 : str = replace(str, "\xE9", "e");
123 3943824 : str = replace(str, "\xC8", "E");
124 3943824 : str = replace(str, "\xE8", "e");
125 1314608 : return str;
126 : }
127 :
128 :
129 : std::string
130 72310267 : StringUtils::replace(std::string str, const std::string& what, const std::string& by) {
131 : std::string::size_type idx = str.find(what);
132 72310267 : const int what_len = (int)what.length();
133 72310267 : if (what_len > 0) {
134 72310266 : const int by_len = (int)by.length();
135 72314193 : while (idx != std::string::npos) {
136 3927 : str = str.replace(idx, what_len, by);
137 3927 : idx = str.find(what, idx + by_len);
138 : }
139 : }
140 72310267 : return str;
141 : }
142 :
143 :
144 : std::string
145 1340182 : StringUtils::substituteEnvironment(const std::string& str, const std::chrono::time_point<std::chrono::system_clock>* const timeRef) {
146 : std::string s = str;
147 1340182 : 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 1340176 : 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 1340182 : 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 1340182 : if (std::getenv("SUMO_LOGO") == nullptr) {
175 4020546 : s = replace(s, "${SUMO_LOGO}", "${SUMO_HOME}/data/logo/sumo-128x138.png");
176 : }
177 : const std::string::size_type tildeIndex = str.find("~");
178 1340182 : if (tildeIndex == 0) {
179 3 : s.replace(0, 1, "${HOME}");
180 : }
181 4020546 : 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 1340182 : 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 1340248 : 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 1340182 : return s;
215 1340182 : }
216 :
217 :
218 : std::string
219 88375 : StringUtils::isoTimeString(const std::chrono::time_point<std::chrono::system_clock>* const timeRef) {
220 88375 : 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 88375 : 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 88375 : std::tm local_tm = *std::localtime(&now_c);
225 :
226 : // Get the time zone offset
227 88375 : std::time_t utc_time = std::time(nullptr);
228 88375 : std::tm utc_tm = *std::gmtime(&utc_time);
229 88375 : const double offset = std::difftime(std::mktime(&local_tm), std::mktime(&utc_tm)) / 3600.0;
230 88375 : const int hours_offset = static_cast<int>(offset);
231 88375 : const int minutes_offset = static_cast<int>((offset - hours_offset) * 60);
232 :
233 : // Format the time
234 88375 : std::ostringstream oss;
235 : char buf[32];
236 88375 : 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 88375 : << std::setw(2) << std::setfill('0') << std::abs(hours_offset) << ":"
241 176750 : << std::setw(2) << std::setfill('0') << std::abs(minutes_offset);
242 88375 : return oss.str();
243 88375 : }
244 :
245 :
246 : bool
247 6720067 : StringUtils::startsWith(const std::string& str, const std::string prefix) {
248 6720067 : return str.compare(0, prefix.length(), prefix) == 0;
249 : }
250 :
251 :
252 : bool
253 224477 : StringUtils::endsWith(const std::string& str, const std::string suffix) {
254 224477 : if (str.length() >= suffix.length()) {
255 224454 : 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 1504861 : StringUtils::escapeXML(const std::string& orig, const bool maskDoubleHyphen) {
270 4514583 : std::string result = replace(orig, "&", "&");
271 4514583 : result = replace(result, ">", ">");
272 4514583 : result = replace(result, "<", "<");
273 4514583 : result = replace(result, "\"", """);
274 1504861 : if (maskDoubleHyphen) {
275 2342838 : result = replace(result, "--", "--");
276 : }
277 48155552 : for (char invalid = '\1'; invalid < ' '; invalid++) {
278 233253455 : result = replace(result, std::string(1, invalid).c_str(), "");
279 : }
280 4514583 : return replace(result, "'", "'");
281 : }
282 :
283 :
284 : std::string
285 0 : StringUtils::escapeShell(const std::string& orig) {
286 0 : return replace(orig, "\"", "\\\"");
287 : }
288 :
289 :
290 : std::string
291 4072 : StringUtils::escapeCSV(const std::string& orig, const char separator, const char quote) {
292 4072 : const std::string chars{separator, quote};
293 4072 : if (orig.find_first_of(chars) == std::string::npos) {
294 : return orig;
295 : }
296 36 : const std::string quoteStr{quote};
297 108 : return quoteStr + replace(orig, quoteStr, {'\\', quote}) + quoteStr;
298 : }
299 :
300 :
301 : std::string
302 5121 : StringUtils::urlEncode(const std::string& toEncode, const std::string encodeWhich) {
303 5121 : std::ostringstream out;
304 :
305 131266 : for (int i = 0; i < (int)toEncode.length(); ++i) {
306 126145 : const char t = toEncode.at(i);
307 :
308 126145 : if ((encodeWhich != "" && encodeWhich.find(t) == std::string::npos) ||
309 1 : (encodeWhich == "" &&
310 0 : ((t >= 45 && t <= 57) || // hyphen, period, slash, 0-9
311 0 : (t >= 65 && t <= 90) || // A-Z
312 : t == 95 || // underscore
313 : (t >= 97 && t <= 122) || // a-z
314 : t == 126)) // tilde
315 : ) {
316 126144 : out << toEncode.at(i);
317 : } else {
318 2 : out << charToHex(toEncode.at(i));
319 : }
320 : }
321 :
322 5121 : return out.str();
323 5121 : }
324 :
325 :
326 : std::string
327 92738 : StringUtils::urlDecode(const std::string& toDecode) {
328 92738 : std::ostringstream out;
329 :
330 1971581 : for (int i = 0; i < (int)toDecode.length(); ++i) {
331 1878845 : if (toDecode.at(i) == '%') {
332 2 : std::string str(toDecode.substr(i + 1, 2));
333 2 : out << hexToChar(str);
334 0 : i += 2;
335 : } else {
336 1878843 : out << toDecode.at(i);
337 : }
338 : }
339 :
340 92736 : return out.str();
341 92738 : }
342 :
343 : std::string
344 1 : StringUtils::charToHex(unsigned char c) {
345 : short i = c;
346 :
347 1 : std::stringstream s;
348 :
349 1 : s << "%" << std::setw(2) << std::setfill('0') << std::hex << i;
350 :
351 1 : return s.str();
352 1 : }
353 :
354 :
355 : unsigned char
356 2 : StringUtils::hexToChar(const std::string& str) {
357 2 : short c = 0;
358 2 : if (!str.empty()) {
359 2 : std::istringstream in(str);
360 2 : in >> std::hex >> c;
361 2 : if (in.fail()) {
362 4 : throw NumberFormatException(str + " could not be interpreted as hex");
363 : }
364 2 : }
365 0 : return static_cast<unsigned char>(c);
366 : }
367 :
368 :
369 : int
370 19144172 : StringUtils::toInt(const std::string& sData) {
371 19144172 : long long int result = toLong(sData);
372 19126704 : if (result > std::numeric_limits<int>::max() || result < std::numeric_limits<int>::min()) {
373 3 : throw NumberFormatException(toString(result) + " int overflow");
374 : }
375 19126703 : return (int)result;
376 : }
377 :
378 :
379 : bool
380 8 : StringUtils::isInt(const std::string& sData) {
381 : // first check if can be converted to long int
382 8 : if (isLong(sData)) {
383 0 : const long long int result = toLong(sData);
384 : // now check if the result is in the range of an int
385 0 : return ((result <= std::numeric_limits<int>::max()) && (result >= std::numeric_limits<int>::min()));
386 : }
387 : return false;
388 : }
389 :
390 :
391 : int
392 0 : StringUtils::toIntSecure(const std::string& sData, int def) {
393 0 : if (sData.length() == 0) {
394 : return def;
395 : }
396 0 : return toInt(sData);
397 : }
398 :
399 :
400 : long long int
401 20588135 : StringUtils::toLong(const std::string& sData) {
402 : const char* const data = sData.c_str();
403 20588135 : if (data == 0 || data[0] == 0) {
404 64 : throw EmptyData();
405 : }
406 : char* end;
407 20588071 : errno = 0;
408 : #ifdef _MSC_VER
409 : long long int ret = _strtoi64(data, &end, 10);
410 : #else
411 20588071 : long long int ret = strtoll(data, &end, 10);
412 : #endif
413 20588071 : if (errno == ERANGE) {
414 0 : errno = 0;
415 0 : throw NumberFormatException("(long long integer range) " + sData);
416 : }
417 20588071 : if ((int)(end - data) != (int)strlen(data)) {
418 36054 : throw NumberFormatException("(long long integer format) " + sData);
419 : }
420 20570044 : return ret;
421 : }
422 :
423 :
424 : bool
425 8 : StringUtils::isLong(const std::string& sData) {
426 : const char* const data = sData.c_str();
427 8 : if (data == 0 || data[0] == 0) {
428 : return false;
429 : }
430 : char* end;
431 : // reset errno before parsing, to keep errors
432 8 : errno = 0;
433 : // continue depending of current plattform
434 : #ifdef _MSC_VER
435 : _strtoi64(data, &end, 10);
436 : #else
437 8 : strtoll(data, &end, 10);
438 : #endif
439 : // check out of range
440 8 : if (errno == ERANGE) {
441 : return false;
442 : }
443 : // check length of converted data
444 8 : if ((int)(end - data) != (int)strlen(data)) {
445 : return false;
446 : }
447 : return true;
448 : }
449 :
450 :
451 : int
452 968 : StringUtils::hexToInt(const std::string& sData) {
453 968 : if (sData.length() == 0) {
454 0 : throw EmptyData();
455 : }
456 968 : size_t idx = 0;
457 : int result;
458 : try {
459 968 : if (sData[0] == '#') { // for html color codes
460 884 : result = std::stoi(sData.substr(1), &idx, 16);
461 884 : idx++;
462 : } else {
463 : result = std::stoi(sData, &idx, 16);
464 : }
465 0 : } catch (...) {
466 0 : throw NumberFormatException("(hex integer format) " + sData);
467 0 : }
468 968 : if (idx != sData.length()) {
469 0 : throw NumberFormatException("(hex integer format) " + sData);
470 : }
471 968 : return result;
472 : }
473 :
474 :
475 : bool
476 0 : StringUtils::isHex(std::string sData) {
477 0 : if (sData.length() == 0) {
478 : return false;
479 : }
480 : // remove the first character (for HTML color codes)
481 0 : if (sData[0] == '#') {
482 0 : sData = sData.substr(1);
483 : }
484 : const char* sDataPtr = sData.c_str();
485 : char* returnPtr;
486 : // reset errno
487 0 : errno = 0;
488 : // call string to long (size 16) from standard library
489 0 : strtol(sDataPtr, &returnPtr, 16);
490 : // check out of range
491 0 : if (errno == ERANGE) {
492 : return false;
493 : }
494 : // check if there was an error converting sDataPtr to double,
495 0 : if (sDataPtr == returnPtr) {
496 : return false;
497 : }
498 : // compare size of start and end points
499 0 : if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
500 : return false;
501 : }
502 : return true;
503 : }
504 :
505 :
506 : double
507 76541635 : StringUtils::toDouble(const std::string& sData) {
508 76541635 : if (sData.size() == 0) {
509 191 : throw EmptyData();
510 : }
511 : try {
512 76541444 : size_t idx = 0;
513 : const double result = std::stod(sData, &idx);
514 76539863 : if (idx != sData.size()) {
515 34 : throw NumberFormatException("(double format) " + sData);
516 : } else {
517 76539846 : return result;
518 : }
519 1598 : } catch (...) {
520 : // invalid_argument or out_of_range
521 3196 : throw NumberFormatException("(double) " + sData);
522 1598 : }
523 : }
524 :
525 :
526 : bool
527 0 : StringUtils::isDouble(const std::string& sData) {
528 0 : if (sData.size() == 0) {
529 : return false;
530 : }
531 : const char* sDataPtr = sData.c_str();
532 : char* returnPtr;
533 : // reset errno
534 0 : errno = 0;
535 : // call string to double from standard library
536 0 : strtod(sDataPtr, &returnPtr);
537 : // check out of range
538 0 : if (errno == ERANGE) {
539 : return false;
540 : }
541 : // check if there was an error converting sDataPtr to double,
542 0 : if (sDataPtr == returnPtr) {
543 : return false;
544 : }
545 : // compare size of start and end points
546 0 : if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
547 : return false;
548 : }
549 : return true;
550 : }
551 :
552 :
553 : double
554 368 : StringUtils::toDoubleSecure(const std::string& sData, const double def) {
555 368 : if (sData.length() == 0) {
556 : return def;
557 : }
558 368 : return toDouble(sData);
559 : }
560 :
561 :
562 : bool
563 3348393 : StringUtils::toBool(const std::string& sData) {
564 3348393 : if (sData.length() == 0) {
565 1 : throw EmptyData();
566 : }
567 3348392 : const std::string s = to_lower_case(sData);
568 3348392 : if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
569 : return true;
570 : }
571 2704265 : if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
572 : return false;
573 : }
574 147 : throw BoolFormatException(s);
575 : }
576 :
577 :
578 : bool
579 75642 : StringUtils::isBool(const std::string& sData) {
580 75642 : if (sData.length() == 0) {
581 : return false;
582 : }
583 17628 : const std::string s = to_lower_case(sData);
584 : // check true values
585 17628 : if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
586 : return true;
587 : }
588 : // check false values
589 1064 : if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
590 : return true;
591 : }
592 : // no valid true or false values
593 : return false;
594 : }
595 :
596 :
597 : MMVersion
598 47883 : StringUtils::toVersion(const std::string& sData) {
599 143649 : std::vector<std::string> parts = StringTokenizer(sData, ".").getVector();
600 95766 : return MMVersion(toInt(parts.front()), toDouble(parts.back()));
601 47883 : }
602 :
603 :
604 : double
605 2287 : StringUtils::parseDist(const std::string& sData) {
606 2287 : if (sData.size() == 0) {
607 1 : throw EmptyData();
608 : }
609 : try {
610 2286 : size_t idx = 0;
611 : const double result = std::stod(sData, &idx);
612 2285 : if (idx != sData.size()) {
613 5 : const std::string unit = prune(sData.substr(idx));
614 5 : if (unit == "m" || unit == "metre" || unit == "meter" || unit == "metres" || unit == "meters") {
615 : return result;
616 : }
617 2 : if (unit == "km" || unit == "kilometre" || unit == "kilometer" || unit == "kilometres" || unit == "kilometers") {
618 1 : return result * 1000.;
619 : }
620 1 : if (unit == "mi" || unit == "mile" || unit == "miles") {
621 0 : return result * 1000. * KM_PER_MILE;
622 : }
623 1 : if (unit == "nmi") {
624 0 : return result * 1852.;
625 : }
626 1 : if (unit == "ft" || unit == "foot" || unit == "feet") {
627 0 : return result * 12. * 0.0254;
628 : }
629 1 : if (unit == "\"" || unit == "in" || unit == "inch" || unit == "inches") {
630 0 : return result * 0.0254;
631 : }
632 1 : if (unit[0] == '\'') {
633 0 : double inches = 12 * result;
634 0 : if (unit.length() > 1) {
635 0 : inches += std::stod(unit.substr(1), &idx);
636 1 : if (unit.substr(idx) == "\"") {
637 0 : return inches * 0.0254;
638 : }
639 : }
640 : }
641 2 : throw NumberFormatException("(distance format) " + sData);
642 : } else {
643 : return result;
644 : }
645 2 : } catch (...) {
646 : // invalid_argument or out_of_range
647 4 : throw NumberFormatException("(double) " + sData);
648 2 : }
649 : }
650 :
651 :
652 : double
653 8129 : StringUtils::parseSpeed(const std::string& sData, const bool defaultKmph) {
654 8129 : if (sData.size() == 0) {
655 1 : throw EmptyData();
656 : }
657 : try {
658 8128 : size_t idx = 0;
659 : const double result = std::stod(sData, &idx);
660 8124 : if (idx != sData.size()) {
661 1247 : const std::string unit = prune(sData.substr(idx));
662 1247 : if (unit == "km/h" || unit == "kph" || unit == "kmh" || unit == "kmph") {
663 3 : return result / 3.6;
664 : }
665 1244 : if (unit == "m/s") {
666 : return result;
667 : }
668 1242 : if (unit == "mph") {
669 1241 : return result * KM_PER_MILE / 3.6;
670 : }
671 1 : if (unit == "knots") {
672 0 : return result * 1.852 / 3.6;
673 : }
674 2 : throw NumberFormatException("(speed format) " + sData);
675 : } else {
676 6877 : return defaultKmph ? result / 3.6 : result;
677 : }
678 5 : } catch (...) {
679 : // invalid_argument or out_of_range
680 10 : throw NumberFormatException("(double) " + sData);
681 5 : }
682 : }
683 :
684 :
685 :
686 : std::string
687 0 : StringUtils::trim_left(const std::string s, const std::string& t) {
688 : std::string result = s;
689 0 : result.erase(0, s.find_first_not_of(t));
690 0 : return result;
691 : }
692 :
693 : std::string
694 0 : StringUtils::trim_right(const std::string s, const std::string& t) {
695 : std::string result = s;
696 0 : result.erase(s.find_last_not_of(t) + 1);
697 0 : return result;
698 : }
699 :
700 : std::string
701 0 : StringUtils::trim(const std::string s, const std::string& t) {
702 0 : return trim_right(trim_left(s, t), t);
703 : }
704 :
705 :
706 : std::string
707 0 : StringUtils::wrapText(const std::string s, int width) {
708 0 : std::vector<std::string> parts = StringTokenizer(s).getVector();
709 : std::string result;
710 : std::string line;
711 : bool firstLine = true;
712 : bool firstWord = true;
713 0 : for (std::string p : parts) {
714 0 : if ((int)(line.size() + p.size()) < width || firstWord) {
715 0 : if (firstWord) {
716 : firstWord = false;
717 : } else {
718 : line += " ";
719 : }
720 : line += p;
721 : } else {
722 0 : if (firstLine) {
723 : firstLine = false;
724 : } else {
725 : result += "\n";
726 : }
727 : result += line;
728 : line.clear();
729 : line += p;
730 : }
731 : }
732 0 : if (line.size() > 0) {
733 0 : if (firstLine) {
734 : firstLine = false;
735 : } else {
736 : result += "\n";
737 : }
738 : result += line;
739 : }
740 0 : return result;
741 0 : }
742 :
743 :
744 : std::string
745 966 : StringUtils::adjustDecimalValue(double value, int precision) {
746 : // obtain value in string format with 20 decimals precision
747 966 : auto valueStr = toString(value, precision);
748 : // now clear all zeros
749 16970 : while (valueStr.size() > 1) {
750 16970 : if (valueStr.back() == '0') {
751 : valueStr.pop_back();
752 966 : } else if (valueStr.back() == '.') {
753 : valueStr.pop_back();
754 : return valueStr;
755 : } else {
756 : return valueStr;
757 : }
758 : }
759 : return valueStr;
760 : }
761 :
762 :
763 : /****************************************************************************/
|