Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/common/StringUtils.cpp
193874 views
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 <xercesc/util/TransService.hpp>
38
#include <xercesc/util/TranscodingException.hpp>
39
#include <utils/common/UtilExceptions.h>
40
#include <utils/common/ToString.h>
41
#include <utils/common/StringTokenizer.h>
42
#include "StringUtils.h"
43
44
#define KM_PER_MILE 1.609344
45
46
47
// ===========================================================================
48
// static member definitions
49
// ===========================================================================
50
std::string StringUtils::emptyString;
51
XERCES_CPP_NAMESPACE::XMLLCPTranscoder* StringUtils::myLCPTranscoder = nullptr;
52
53
54
// ===========================================================================
55
// method definitions
56
// ===========================================================================
57
std::string
58
StringUtils::prune(const std::string& str) {
59
const std::string::size_type endpos = str.find_last_not_of(" \t\n\r");
60
if (std::string::npos != endpos) {
61
const int startpos = (int)str.find_first_not_of(" \t\n\r");
62
return str.substr(startpos, endpos - startpos + 1);
63
}
64
return "";
65
}
66
67
68
std::string
69
StringUtils::pruneZeros(const std::string& str, int max) {
70
const std::string::size_type endpos = str.find_last_not_of("0");
71
if (endpos != std::string::npos && str.back() == '0') {
72
std::string res = str.substr(0, MAX2((int)str.size() - max, (int)endpos + 1));
73
return res;
74
}
75
return str;
76
}
77
78
std::string
79
StringUtils::to_lower_case(const std::string& str) {
80
std::string s = str;
81
std::transform(s.begin(), s.end(), s.begin(), [](char c) {
82
return (char)::tolower(c);
83
});
84
return s;
85
}
86
87
88
std::string
89
StringUtils::latin1_to_utf8(std::string str) {
90
// inspired by http://stackoverflow.com/questions/4059775/convert-iso-8859-1-strings-to-utf-8-in-c-c
91
std::string result;
92
for (const auto& c : str) {
93
const unsigned char uc = (unsigned char)c;
94
if (uc < 128) {
95
result += uc;
96
} else {
97
result += (char)(0xc2 + (uc > 0xbf));
98
result += (char)((uc & 0x3f) + 0x80);
99
}
100
}
101
return result;
102
}
103
104
105
std::string
106
StringUtils::convertUmlaute(std::string str) {
107
str = replace(str, "\xE4", "ae");
108
str = replace(str, "\xC4", "Ae");
109
str = replace(str, "\xF6", "oe");
110
str = replace(str, "\xD6", "Oe");
111
str = replace(str, "\xFC", "ue");
112
str = replace(str, "\xDC", "Ue");
113
str = replace(str, "\xDF", "ss");
114
str = replace(str, "\xC9", "E");
115
str = replace(str, "\xE9", "e");
116
str = replace(str, "\xC8", "E");
117
str = replace(str, "\xE8", "e");
118
return str;
119
}
120
121
122
std::string
123
StringUtils::replace(std::string str, const std::string& what, const std::string& by) {
124
std::string::size_type idx = str.find(what);
125
const int what_len = (int)what.length();
126
if (what_len > 0) {
127
const int by_len = (int)by.length();
128
while (idx != std::string::npos) {
129
str = str.replace(idx, what_len, by);
130
idx = str.find(what, idx + by_len);
131
}
132
}
133
return str;
134
}
135
136
137
std::string
138
StringUtils::substituteEnvironment(const std::string& str, const std::chrono::time_point<std::chrono::system_clock>* const timeRef) {
139
std::string s = str;
140
if (timeRef != nullptr) {
141
const std::string::size_type localTimeIndex = str.find("${LOCALTIME}");
142
const std::string::size_type utcIndex = str.find("${UTC}");
143
const bool isUTC = utcIndex != std::string::npos;
144
if (localTimeIndex != std::string::npos || isUTC) {
145
const time_t rawtime = std::chrono::system_clock::to_time_t(*timeRef);
146
char buffer [80];
147
struct tm* timeinfo = isUTC ? gmtime(&rawtime) : localtime(&rawtime);
148
strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S.", timeinfo);
149
auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(*timeRef);
150
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(*timeRef - seconds);
151
const std::string micro = buffer + toString(microseconds.count());
152
if (isUTC) {
153
s.replace(utcIndex, 6, micro);
154
} else {
155
s.replace(localTimeIndex, 12, micro);
156
}
157
}
158
}
159
const std::string::size_type pidIndex = str.find("${PID}");
160
if (pidIndex != std::string::npos) {
161
#ifdef WIN32
162
s.replace(pidIndex, 6, toString(::GetCurrentProcessId()));
163
#else
164
s.replace(pidIndex, 6, toString(::getpid()));
165
#endif
166
}
167
if (std::getenv("SUMO_LOGO") == nullptr) {
168
s = replace(s, "${SUMO_LOGO}", "${SUMO_HOME}/data/logo/sumo-128x138.png");
169
}
170
const std::string::size_type tildeIndex = str.find("~");
171
if (tildeIndex == 0) {
172
s.replace(0, 1, "${HOME}");
173
}
174
s = replace(s, ",~", ",${HOME}");
175
#ifdef WIN32
176
if (std::getenv("HOME") == nullptr) {
177
s = replace(s, "${HOME}", "${USERPROFILE}");
178
}
179
#endif
180
181
// Expression for an environment variables, e.g. ${NAME}
182
// Note: - R"(...)" is a raw string literal syntax to simplify a regex declaration
183
// - .+? looks for the shortest match (non-greedy)
184
// - (.+?) defines a "subgroup" which is already stripped of the $ and {, }
185
std::regex envVarExpr(R"(\$\{(.+?)\})");
186
187
// Are there any variables in this string?
188
std::smatch match;
189
std::string strIter = s;
190
191
// Loop over the entire value string and look for variable names
192
while (std::regex_search(strIter, match, envVarExpr)) {
193
std::string varName = match[1];
194
195
// Find the variable in the environment and its value
196
std::string varValue;
197
if (std::getenv(varName.c_str()) != nullptr) {
198
varValue = std::getenv(varName.c_str());
199
}
200
201
// Replace the variable placeholder with its value in the original string
202
s = std::regex_replace(s, std::regex("\\$\\{" + varName + "\\}"), varValue);
203
204
// Continue the loop with the remainder of the string
205
strIter = match.suffix();
206
}
207
return s;
208
}
209
210
211
std::string
212
StringUtils::isoTimeString(const std::chrono::time_point<std::chrono::system_clock>* const timeRef) {
213
const std::chrono::system_clock::time_point now = timeRef == nullptr ? std::chrono::system_clock::now() : *timeRef;
214
const auto now_seconds = std::chrono::time_point_cast<std::chrono::seconds>(now);
215
const std::time_t now_c = std::chrono::system_clock::to_time_t(now);
216
const auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - now_seconds).count();
217
std::tm local_tm = *std::localtime(&now_c);
218
219
// Get the time zone offset
220
std::time_t utc_time = std::time(nullptr);
221
std::tm utc_tm = *std::gmtime(&utc_time);
222
const double offset = std::difftime(std::mktime(&local_tm), std::mktime(&utc_tm)) / 3600.0;
223
const int hours_offset = static_cast<int>(offset);
224
const int minutes_offset = static_cast<int>((offset - hours_offset) * 60);
225
226
// Format the time
227
std::ostringstream oss;
228
char buf[32];
229
std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &local_tm);
230
oss << buf << "."
231
<< std::setw(6) << std::setfill('0') << std::abs(microseconds)
232
<< (hours_offset >= 0 ? "+" : "-")
233
<< std::setw(2) << std::setfill('0') << std::abs(hours_offset) << ":"
234
<< std::setw(2) << std::setfill('0') << std::abs(minutes_offset);
235
return oss.str();
236
}
237
238
239
bool
240
StringUtils::startsWith(const std::string& str, const std::string prefix) {
241
return str.compare(0, prefix.length(), prefix) == 0;
242
}
243
244
245
bool
246
StringUtils::endsWith(const std::string& str, const std::string suffix) {
247
if (str.length() >= suffix.length()) {
248
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
249
} else {
250
return false;
251
}
252
}
253
254
255
std::string
256
StringUtils::padFront(const std::string& str, int length, char padding) {
257
return std::string(MAX2(0, length - (int)str.size()), padding) + str;
258
}
259
260
261
std::string
262
StringUtils::escapeXML(const std::string& orig, const bool maskDoubleHyphen) {
263
std::string result = replace(orig, "&", "&amp;");
264
result = replace(result, ">", "&gt;");
265
result = replace(result, "<", "&lt;");
266
result = replace(result, "\"", "&quot;");
267
if (maskDoubleHyphen) {
268
result = replace(result, "--", "&#45;&#45;");
269
}
270
for (char invalid = '\1'; invalid < ' '; invalid++) {
271
result = replace(result, std::string(1, invalid).c_str(), "");
272
}
273
return replace(result, "'", "&apos;");
274
}
275
276
277
std::string
278
StringUtils::escapeShell(const std::string& orig) {
279
std::string result = replace(orig, "\"", "\\\"");
280
return result;
281
}
282
283
284
std::string
285
StringUtils::urlEncode(const std::string& toEncode, const std::string encodeWhich) {
286
std::ostringstream out;
287
288
for (int i = 0; i < (int)toEncode.length(); ++i) {
289
const char t = toEncode.at(i);
290
291
if ((encodeWhich != "" && encodeWhich.find(t) == std::string::npos) ||
292
(encodeWhich == "" &&
293
((t >= 45 && t <= 57) || // hyphen, period, slash, 0-9
294
(t >= 65 && t <= 90) || // A-Z
295
t == 95 || // underscore
296
(t >= 97 && t <= 122) || // a-z
297
t == 126)) // tilde
298
) {
299
out << toEncode.at(i);
300
} else {
301
out << charToHex(toEncode.at(i));
302
}
303
}
304
305
return out.str();
306
}
307
308
309
std::string
310
StringUtils::urlDecode(const std::string& toDecode) {
311
std::ostringstream out;
312
313
for (int i = 0; i < (int)toDecode.length(); ++i) {
314
if (toDecode.at(i) == '%') {
315
std::string str(toDecode.substr(i + 1, 2));
316
out << hexToChar(str);
317
i += 2;
318
} else {
319
out << toDecode.at(i);
320
}
321
}
322
323
return out.str();
324
}
325
326
std::string
327
StringUtils::charToHex(unsigned char c) {
328
short i = c;
329
330
std::stringstream s;
331
332
s << "%" << std::setw(2) << std::setfill('0') << std::hex << i;
333
334
return s.str();
335
}
336
337
338
unsigned char
339
StringUtils::hexToChar(const std::string& str) {
340
short c = 0;
341
if (!str.empty()) {
342
std::istringstream in(str);
343
in >> std::hex >> c;
344
if (in.fail()) {
345
throw NumberFormatException(str + " could not be interpreted as hex");
346
}
347
}
348
return static_cast<unsigned char>(c);
349
}
350
351
352
int
353
StringUtils::toInt(const std::string& sData) {
354
long long int result = toLong(sData);
355
if (result > std::numeric_limits<int>::max() || result < std::numeric_limits<int>::min()) {
356
throw NumberFormatException(toString(result) + " int overflow");
357
}
358
return (int)result;
359
}
360
361
362
bool
363
StringUtils::isInt(const std::string& sData) {
364
// first check if can be converted to long int
365
if (isLong(sData)) {
366
const long long int result = toLong(sData);
367
// now check if the result is in the range of an int
368
return ((result <= std::numeric_limits<int>::max()) && (result >= std::numeric_limits<int>::min()));
369
}
370
return false;
371
}
372
373
374
int
375
StringUtils::toIntSecure(const std::string& sData, int def) {
376
if (sData.length() == 0) {
377
return def;
378
}
379
return toInt(sData);
380
}
381
382
383
long long int
384
StringUtils::toLong(const std::string& sData) {
385
const char* const data = sData.c_str();
386
if (data == 0 || data[0] == 0) {
387
throw EmptyData();
388
}
389
char* end;
390
errno = 0;
391
#ifdef _MSC_VER
392
long long int ret = _strtoi64(data, &end, 10);
393
#else
394
long long int ret = strtoll(data, &end, 10);
395
#endif
396
if (errno == ERANGE) {
397
errno = 0;
398
throw NumberFormatException("(long long integer range) " + sData);
399
}
400
if ((int)(end - data) != (int)strlen(data)) {
401
throw NumberFormatException("(long long integer format) " + sData);
402
}
403
return ret;
404
}
405
406
407
bool
408
StringUtils::isLong(const std::string& sData) {
409
const char* const data = sData.c_str();
410
if (data == 0 || data[0] == 0) {
411
return false;
412
}
413
char* end;
414
// reset errno before parsing, to keep errors
415
errno = 0;
416
// continue depending of current plattform
417
#ifdef _MSC_VER
418
_strtoi64(data, &end, 10);
419
#else
420
strtoll(data, &end, 10);
421
#endif
422
// check out of range
423
if (errno == ERANGE) {
424
return false;
425
}
426
// check length of converted data
427
if ((int)(end - data) != (int)strlen(data)) {
428
return false;
429
}
430
return true;
431
}
432
433
434
int
435
StringUtils::hexToInt(const std::string& sData) {
436
if (sData.length() == 0) {
437
throw EmptyData();
438
}
439
size_t idx = 0;
440
int result;
441
try {
442
if (sData[0] == '#') { // for html color codes
443
result = std::stoi(sData.substr(1), &idx, 16);
444
idx++;
445
} else {
446
result = std::stoi(sData, &idx, 16);
447
}
448
} catch (...) {
449
throw NumberFormatException("(hex integer format) " + sData);
450
}
451
if (idx != sData.length()) {
452
throw NumberFormatException("(hex integer format) " + sData);
453
}
454
return result;
455
}
456
457
458
bool
459
StringUtils::isHex(std::string sData) {
460
if (sData.length() == 0) {
461
return false;
462
}
463
// remove the first character (for HTML color codes)
464
if (sData[0] == '#') {
465
sData = sData.substr(1);
466
}
467
const char* sDataPtr = sData.c_str();
468
char* returnPtr;
469
// reset errno
470
errno = 0;
471
// call string to long (size 16) from standard library
472
strtol(sDataPtr, &returnPtr, 16);
473
// check out of range
474
if (errno == ERANGE) {
475
return false;
476
}
477
// check if there was an error converting sDataPtr to double,
478
if (sDataPtr == returnPtr) {
479
return false;
480
}
481
// compare size of start and end points
482
if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
483
return false;
484
}
485
return true;
486
}
487
488
489
double
490
StringUtils::toDouble(const std::string& sData) {
491
if (sData.size() == 0) {
492
throw EmptyData();
493
}
494
try {
495
size_t idx = 0;
496
const double result = std::stod(sData, &idx);
497
if (idx != sData.size()) {
498
throw NumberFormatException("(double format) " + sData);
499
} else {
500
return result;
501
}
502
} catch (...) {
503
// invalid_argument or out_of_range
504
throw NumberFormatException("(double) " + sData);
505
}
506
}
507
508
509
bool
510
StringUtils::isDouble(const std::string& sData) {
511
if (sData.size() == 0) {
512
return false;
513
}
514
const char* sDataPtr = sData.c_str();
515
char* returnPtr;
516
// reset errno
517
errno = 0;
518
// call string to double from standard library
519
strtod(sDataPtr, &returnPtr);
520
// check out of range
521
if (errno == ERANGE) {
522
return false;
523
}
524
// check if there was an error converting sDataPtr to double,
525
if (sDataPtr == returnPtr) {
526
return false;
527
}
528
// compare size of start and end points
529
if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
530
return false;
531
}
532
return true;
533
}
534
535
536
double
537
StringUtils::toDoubleSecure(const std::string& sData, const double def) {
538
if (sData.length() == 0) {
539
return def;
540
}
541
return toDouble(sData);
542
}
543
544
545
bool
546
StringUtils::toBool(const std::string& sData) {
547
if (sData.length() == 0) {
548
throw EmptyData();
549
}
550
const std::string s = to_lower_case(sData);
551
if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
552
return true;
553
}
554
if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
555
return false;
556
}
557
throw BoolFormatException(s);
558
}
559
560
561
bool
562
StringUtils::isBool(const std::string& sData) {
563
if (sData.length() == 0) {
564
return false;
565
}
566
const std::string s = to_lower_case(sData);
567
// check true values
568
if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
569
return true;
570
}
571
// check false values
572
if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
573
return true;
574
}
575
// no valid true or false values
576
return false;
577
}
578
579
580
MMVersion
581
StringUtils::toVersion(const std::string& sData) {
582
std::vector<std::string> parts = StringTokenizer(sData, ".").getVector();
583
return MMVersion(toInt(parts.front()), toDouble(parts.back()));
584
}
585
586
587
double
588
StringUtils::parseDist(const std::string& sData) {
589
if (sData.size() == 0) {
590
throw EmptyData();
591
}
592
try {
593
size_t idx = 0;
594
const double result = std::stod(sData, &idx);
595
if (idx != sData.size()) {
596
const std::string unit = prune(sData.substr(idx));
597
if (unit == "m" || unit == "metre" || unit == "meter" || unit == "metres" || unit == "meters") {
598
return result;
599
}
600
if (unit == "km" || unit == "kilometre" || unit == "kilometer" || unit == "kilometres" || unit == "kilometers") {
601
return result * 1000.;
602
}
603
if (unit == "mi" || unit == "mile" || unit == "miles") {
604
return result * 1000. * KM_PER_MILE;
605
}
606
if (unit == "nmi") {
607
return result * 1852.;
608
}
609
if (unit == "ft" || unit == "foot" || unit == "feet") {
610
return result * 12. * 0.0254;
611
}
612
if (unit == "\"" || unit == "in" || unit == "inch" || unit == "inches") {
613
return result * 0.0254;
614
}
615
if (unit[0] == '\'') {
616
double inches = 12 * result;
617
if (unit.length() > 1) {
618
inches += std::stod(unit.substr(1), &idx);
619
if (unit.substr(idx) == "\"") {
620
return inches * 0.0254;
621
}
622
}
623
}
624
throw NumberFormatException("(distance format) " + sData);
625
} else {
626
return result;
627
}
628
} catch (...) {
629
// invalid_argument or out_of_range
630
throw NumberFormatException("(double) " + sData);
631
}
632
}
633
634
635
double
636
StringUtils::parseSpeed(const std::string& sData, const bool defaultKmph) {
637
if (sData.size() == 0) {
638
throw EmptyData();
639
}
640
try {
641
size_t idx = 0;
642
const double result = std::stod(sData, &idx);
643
if (idx != sData.size()) {
644
const std::string unit = prune(sData.substr(idx));
645
if (unit == "km/h" || unit == "kph" || unit == "kmh" || unit == "kmph") {
646
return result / 3.6;
647
}
648
if (unit == "m/s") {
649
return result;
650
}
651
if (unit == "mph") {
652
return result * KM_PER_MILE / 3.6;
653
}
654
if (unit == "knots") {
655
return result * 1.852 / 3.6;
656
}
657
throw NumberFormatException("(speed format) " + sData);
658
} else {
659
return defaultKmph ? result / 3.6 : result;
660
}
661
} catch (...) {
662
// invalid_argument or out_of_range
663
throw NumberFormatException("(double) " + sData);
664
}
665
}
666
667
668
std::string
669
StringUtils::transcode(const XMLCh* const data, int length) {
670
if (data == 0) {
671
throw EmptyData();
672
}
673
if (length == 0) {
674
return "";
675
}
676
#if _XERCES_VERSION < 30100
677
char* t = XERCES_CPP_NAMESPACE::XMLString::transcode(data);
678
std::string result(t);
679
XERCES_CPP_NAMESPACE::XMLString::release(&t);
680
return result;
681
#else
682
try {
683
XERCES_CPP_NAMESPACE::TranscodeToStr utf8(data, "UTF-8");
684
return reinterpret_cast<const char*>(utf8.str());
685
} catch (XERCES_CPP_NAMESPACE::TranscodingException&) {
686
return "?";
687
}
688
#endif
689
}
690
691
692
std::string
693
StringUtils::transcodeFromLocal(const std::string& localString) {
694
#if _XERCES_VERSION > 30100
695
try {
696
if (myLCPTranscoder == nullptr) {
697
myLCPTranscoder = XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgTransService->makeNewLCPTranscoder(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager);
698
}
699
if (myLCPTranscoder != nullptr) {
700
return transcode(myLCPTranscoder->transcode(localString.c_str()));
701
}
702
} catch (XERCES_CPP_NAMESPACE::TranscodingException&) {}
703
#endif
704
return localString;
705
}
706
707
708
std::string
709
StringUtils::transcodeToLocal(const std::string& utf8String) {
710
#if _XERCES_VERSION > 30100
711
try {
712
if (myLCPTranscoder == nullptr) {
713
myLCPTranscoder = XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgTransService->makeNewLCPTranscoder(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager);
714
}
715
if (myLCPTranscoder != nullptr) {
716
XERCES_CPP_NAMESPACE::TranscodeFromStr utf8(reinterpret_cast<const XMLByte*>(utf8String.c_str()), utf8String.size(), "UTF-8");
717
return myLCPTranscoder->transcode(utf8.str());
718
}
719
} catch (XERCES_CPP_NAMESPACE::TranscodingException&) {}
720
#endif
721
return utf8String;
722
}
723
724
725
std::string
726
StringUtils::trim_left(const std::string s, const std::string& t) {
727
std::string result = s;
728
result.erase(0, s.find_first_not_of(t));
729
return result;
730
}
731
732
std::string
733
StringUtils::trim_right(const std::string s, const std::string& t) {
734
std::string result = s;
735
result.erase(s.find_last_not_of(t) + 1);
736
return result;
737
}
738
739
std::string
740
StringUtils::trim(const std::string s, const std::string& t) {
741
return trim_right(trim_left(s, t), t);
742
}
743
744
745
std::string
746
StringUtils::wrapText(const std::string s, int width) {
747
std::vector<std::string> parts = StringTokenizer(s).getVector();
748
std::string result;
749
std::string line;
750
bool firstLine = true;
751
bool firstWord = true;
752
for (std::string p : parts) {
753
if ((int)(line.size() + p.size()) < width || firstWord) {
754
if (firstWord) {
755
firstWord = false;
756
} else {
757
line += " ";
758
}
759
line = line + p;
760
} else {
761
if (firstLine) {
762
firstLine = false;
763
} else {
764
result += "\n";
765
}
766
result = result + line;
767
line.clear();
768
firstWord = true;
769
}
770
}
771
if (line.size() > 0) {
772
if (firstLine) {
773
firstLine = false;
774
} else {
775
result += "\n";
776
}
777
result = result + line;
778
}
779
return result;
780
}
781
782
783
void
784
StringUtils::resetTranscoder() {
785
myLCPTranscoder = nullptr;
786
}
787
788
789
std::string
790
StringUtils::adjustDecimalValue(double value, int precision) {
791
// obtain value in string format with 20 decimals precision
792
auto valueStr = toString(value, precision);
793
// now clear all zeros
794
while (valueStr.size() > 1) {
795
if (valueStr.back() == '0') {
796
valueStr.pop_back();
797
} else if (valueStr.back() == '.') {
798
valueStr.pop_back();
799
return valueStr;
800
} else {
801
return valueStr;
802
}
803
}
804
return valueStr;
805
}
806
807
/****************************************************************************/
808
809