Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/common/StringUtils.cpp
169678 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 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
oss << std::put_time(&local_tm, "%Y-%m-%dT%H:%M:%S") << "."
229
<< std::setw(6) << std::setfill('0') << std::abs(microseconds)
230
<< (hours_offset >= 0 ? "+" : "-")
231
<< std::setw(2) << std::setfill('0') << std::abs(hours_offset) << ":"
232
<< std::setw(2) << std::setfill('0') << std::abs(minutes_offset);
233
return oss.str();
234
}
235
236
237
bool
238
StringUtils::startsWith(const std::string& str, const std::string prefix) {
239
return str.compare(0, prefix.length(), prefix) == 0;
240
}
241
242
243
bool
244
StringUtils::endsWith(const std::string& str, const std::string suffix) {
245
if (str.length() >= suffix.length()) {
246
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
247
} else {
248
return false;
249
}
250
}
251
252
253
std::string
254
StringUtils::padFront(const std::string& str, int length, char padding) {
255
return std::string(MAX2(0, length - (int)str.size()), padding) + str;
256
}
257
258
259
std::string
260
StringUtils::escapeXML(const std::string& orig, const bool maskDoubleHyphen) {
261
std::string result = replace(orig, "&", "&amp;");
262
result = replace(result, ">", "&gt;");
263
result = replace(result, "<", "&lt;");
264
result = replace(result, "\"", "&quot;");
265
if (maskDoubleHyphen) {
266
result = replace(result, "--", "&#45;&#45;");
267
}
268
for (char invalid = '\1'; invalid < ' '; invalid++) {
269
result = replace(result, std::string(1, invalid).c_str(), "");
270
}
271
return replace(result, "'", "&apos;");
272
}
273
274
275
std::string
276
StringUtils::escapeShell(const std::string& orig) {
277
std::string result = replace(orig, "\"", "\\\"");
278
return result;
279
}
280
281
282
std::string
283
StringUtils::urlEncode(const std::string& toEncode, const std::string encodeWhich) {
284
std::ostringstream out;
285
286
for (int i = 0; i < (int)toEncode.length(); ++i) {
287
const char t = toEncode.at(i);
288
289
if ((encodeWhich != "" && encodeWhich.find(t) == std::string::npos) ||
290
(encodeWhich == "" &&
291
((t >= 45 && t <= 57) || // hyphen, period, slash, 0-9
292
(t >= 65 && t <= 90) || // A-Z
293
t == 95 || // underscore
294
(t >= 97 && t <= 122) || // a-z
295
t == 126)) // tilde
296
) {
297
out << toEncode.at(i);
298
} else {
299
out << charToHex(toEncode.at(i));
300
}
301
}
302
303
return out.str();
304
}
305
306
307
std::string
308
StringUtils::urlDecode(const std::string& toDecode) {
309
std::ostringstream out;
310
311
for (int i = 0; i < (int)toDecode.length(); ++i) {
312
if (toDecode.at(i) == '%') {
313
std::string str(toDecode.substr(i + 1, 2));
314
out << hexToChar(str);
315
i += 2;
316
} else {
317
out << toDecode.at(i);
318
}
319
}
320
321
return out.str();
322
}
323
324
std::string
325
StringUtils::charToHex(unsigned char c) {
326
short i = c;
327
328
std::stringstream s;
329
330
s << "%" << std::setw(2) << std::setfill('0') << std::hex << i;
331
332
return s.str();
333
}
334
335
336
unsigned char
337
StringUtils::hexToChar(const std::string& str) {
338
short c = 0;
339
if (!str.empty()) {
340
std::istringstream in(str);
341
in >> std::hex >> c;
342
if (in.fail()) {
343
throw NumberFormatException(str + " could not be interpreted as hex");
344
}
345
}
346
return static_cast<unsigned char>(c);
347
}
348
349
350
int
351
StringUtils::toInt(const std::string& sData) {
352
long long int result = toLong(sData);
353
if (result > std::numeric_limits<int>::max() || result < std::numeric_limits<int>::min()) {
354
throw NumberFormatException(toString(result) + " int overflow");
355
}
356
return (int)result;
357
}
358
359
360
bool
361
StringUtils::isInt(const std::string& sData) {
362
// first check if can be converted to long int
363
if (isLong(sData)) {
364
const long long int result = toLong(sData);
365
// now check if the result is in the range of an int
366
return ((result <= std::numeric_limits<int>::max()) && (result >= std::numeric_limits<int>::min()));
367
}
368
return false;
369
}
370
371
372
int
373
StringUtils::toIntSecure(const std::string& sData, int def) {
374
if (sData.length() == 0) {
375
return def;
376
}
377
return toInt(sData);
378
}
379
380
381
long long int
382
StringUtils::toLong(const std::string& sData) {
383
const char* const data = sData.c_str();
384
if (data == 0 || data[0] == 0) {
385
throw EmptyData();
386
}
387
char* end;
388
errno = 0;
389
#ifdef _MSC_VER
390
long long int ret = _strtoi64(data, &end, 10);
391
#else
392
long long int ret = strtoll(data, &end, 10);
393
#endif
394
if (errno == ERANGE) {
395
errno = 0;
396
throw NumberFormatException("(long long integer range) " + sData);
397
}
398
if ((int)(end - data) != (int)strlen(data)) {
399
throw NumberFormatException("(long long integer format) " + sData);
400
}
401
return ret;
402
}
403
404
405
bool
406
StringUtils::isLong(const std::string& sData) {
407
const char* const data = sData.c_str();
408
if (data == 0 || data[0] == 0) {
409
return false;
410
}
411
char* end;
412
// reset errno before parsing, to keep errors
413
errno = 0;
414
// continue depending of current plattform
415
#ifdef _MSC_VER
416
_strtoi64(data, &end, 10);
417
#else
418
strtoll(data, &end, 10);
419
#endif
420
// check out of range
421
if (errno == ERANGE) {
422
return false;
423
}
424
// check length of converted data
425
if ((int)(end - data) != (int)strlen(data)) {
426
return false;
427
}
428
return true;
429
}
430
431
432
int
433
StringUtils::hexToInt(const std::string& sData) {
434
if (sData.length() == 0) {
435
throw EmptyData();
436
}
437
size_t idx = 0;
438
int result;
439
try {
440
if (sData[0] == '#') { // for html color codes
441
result = std::stoi(sData.substr(1), &idx, 16);
442
idx++;
443
} else {
444
result = std::stoi(sData, &idx, 16);
445
}
446
} catch (...) {
447
throw NumberFormatException("(hex integer format) " + sData);
448
}
449
if (idx != sData.length()) {
450
throw NumberFormatException("(hex integer format) " + sData);
451
}
452
return result;
453
}
454
455
456
bool
457
StringUtils::isHex(std::string sData) {
458
if (sData.length() == 0) {
459
return false;
460
}
461
// remove the first character (for HTML color codes)
462
if (sData[0] == '#') {
463
sData = sData.substr(1);
464
}
465
const char* sDataPtr = sData.c_str();
466
char* returnPtr;
467
// reset errno
468
errno = 0;
469
// call string to long (size 16) from standard library
470
strtol(sDataPtr, &returnPtr, 16);
471
// check out of range
472
if (errno == ERANGE) {
473
return false;
474
}
475
// check if there was an error converting sDataPtr to double,
476
if (sDataPtr == returnPtr) {
477
return false;
478
}
479
// compare size of start and end points
480
if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
481
return false;
482
}
483
return true;
484
}
485
486
487
double
488
StringUtils::toDouble(const std::string& sData) {
489
if (sData.size() == 0) {
490
throw EmptyData();
491
}
492
try {
493
size_t idx = 0;
494
const double result = std::stod(sData, &idx);
495
if (idx != sData.size()) {
496
throw NumberFormatException("(double format) " + sData);
497
} else {
498
return result;
499
}
500
} catch (...) {
501
// invalid_argument or out_of_range
502
throw NumberFormatException("(double) " + sData);
503
}
504
}
505
506
507
bool
508
StringUtils::isDouble(const std::string& sData) {
509
if (sData.size() == 0) {
510
return false;
511
}
512
const char* sDataPtr = sData.c_str();
513
char* returnPtr;
514
// reset errno
515
errno = 0;
516
// call string to double from standard library
517
strtod(sDataPtr, &returnPtr);
518
// check out of range
519
if (errno == ERANGE) {
520
return false;
521
}
522
// check if there was an error converting sDataPtr to double,
523
if (sDataPtr == returnPtr) {
524
return false;
525
}
526
// compare size of start and end points
527
if (static_cast<size_t>(returnPtr - sDataPtr) != sData.size()) {
528
return false;
529
}
530
return true;
531
}
532
533
534
double
535
StringUtils::toDoubleSecure(const std::string& sData, const double def) {
536
if (sData.length() == 0) {
537
return def;
538
}
539
return toDouble(sData);
540
}
541
542
543
bool
544
StringUtils::toBool(const std::string& sData) {
545
if (sData.length() == 0) {
546
throw EmptyData();
547
}
548
const std::string s = to_lower_case(sData);
549
if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
550
return true;
551
}
552
if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
553
return false;
554
}
555
throw BoolFormatException(s);
556
}
557
558
559
bool
560
StringUtils::isBool(const std::string& sData) {
561
if (sData.length() == 0) {
562
return false;
563
}
564
const std::string s = to_lower_case(sData);
565
// check true values
566
if (s == "1" || s == "yes" || s == "true" || s == "on" || s == "x" || s == "t") {
567
return true;
568
}
569
// check false values
570
if (s == "0" || s == "no" || s == "false" || s == "off" || s == "-" || s == "f") {
571
return true;
572
}
573
// no valid true or false values
574
return false;
575
}
576
577
578
MMVersion
579
StringUtils::toVersion(const std::string& sData) {
580
std::vector<std::string> parts = StringTokenizer(sData, ".").getVector();
581
return MMVersion(toInt(parts.front()), toDouble(parts.back()));
582
}
583
584
585
double
586
StringUtils::parseDist(const std::string& sData) {
587
if (sData.size() == 0) {
588
throw EmptyData();
589
}
590
try {
591
size_t idx = 0;
592
const double result = std::stod(sData, &idx);
593
if (idx != sData.size()) {
594
const std::string unit = prune(sData.substr(idx));
595
if (unit == "m" || unit == "metre" || unit == "meter" || unit == "metres" || unit == "meters") {
596
return result;
597
}
598
if (unit == "km" || unit == "kilometre" || unit == "kilometer" || unit == "kilometres" || unit == "kilometers") {
599
return result * 1000.;
600
}
601
if (unit == "mi" || unit == "mile" || unit == "miles") {
602
return result * 1000. * KM_PER_MILE;
603
}
604
if (unit == "nmi") {
605
return result * 1852.;
606
}
607
if (unit == "ft" || unit == "foot" || unit == "feet") {
608
return result * 12. * 0.0254;
609
}
610
if (unit == "\"" || unit == "in" || unit == "inch" || unit == "inches") {
611
return result * 0.0254;
612
}
613
if (unit[0] == '\'') {
614
double inches = 12 * result;
615
if (unit.length() > 1) {
616
inches += std::stod(unit.substr(1), &idx);
617
if (unit.substr(idx) == "\"") {
618
return inches * 0.0254;
619
}
620
}
621
}
622
throw NumberFormatException("(distance format) " + sData);
623
} else {
624
return result;
625
}
626
} catch (...) {
627
// invalid_argument or out_of_range
628
throw NumberFormatException("(double) " + sData);
629
}
630
}
631
632
633
double
634
StringUtils::parseSpeed(const std::string& sData, const bool defaultKmph) {
635
if (sData.size() == 0) {
636
throw EmptyData();
637
}
638
try {
639
size_t idx = 0;
640
const double result = std::stod(sData, &idx);
641
if (idx != sData.size()) {
642
const std::string unit = prune(sData.substr(idx));
643
if (unit == "km/h" || unit == "kph" || unit == "kmh" || unit == "kmph") {
644
return result / 3.6;
645
}
646
if (unit == "m/s") {
647
return result;
648
}
649
if (unit == "mph") {
650
return result * KM_PER_MILE / 3.6;
651
}
652
if (unit == "knots") {
653
return result * 1.852 / 3.6;
654
}
655
throw NumberFormatException("(speed format) " + sData);
656
} else {
657
return defaultKmph ? result / 3.6 : result;
658
}
659
} catch (...) {
660
// invalid_argument or out_of_range
661
throw NumberFormatException("(double) " + sData);
662
}
663
}
664
665
666
std::string
667
StringUtils::transcode(const XMLCh* const data, int length) {
668
if (data == 0) {
669
throw EmptyData();
670
}
671
if (length == 0) {
672
return "";
673
}
674
#if _XERCES_VERSION < 30100
675
char* t = XERCES_CPP_NAMESPACE::XMLString::transcode(data);
676
std::string result(t);
677
XERCES_CPP_NAMESPACE::XMLString::release(&t);
678
return result;
679
#else
680
try {
681
XERCES_CPP_NAMESPACE::TranscodeToStr utf8(data, "UTF-8");
682
return reinterpret_cast<const char*>(utf8.str());
683
} catch (XERCES_CPP_NAMESPACE::TranscodingException&) {
684
return "?";
685
}
686
#endif
687
}
688
689
690
std::string
691
StringUtils::transcodeFromLocal(const std::string& localString) {
692
#if _XERCES_VERSION > 30100
693
try {
694
if (myLCPTranscoder == nullptr) {
695
myLCPTranscoder = XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgTransService->makeNewLCPTranscoder(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager);
696
}
697
if (myLCPTranscoder != nullptr) {
698
return transcode(myLCPTranscoder->transcode(localString.c_str()));
699
}
700
} catch (XERCES_CPP_NAMESPACE::TranscodingException&) {}
701
#endif
702
return localString;
703
}
704
705
706
std::string
707
StringUtils::transcodeToLocal(const std::string& utf8String) {
708
#if _XERCES_VERSION > 30100
709
try {
710
if (myLCPTranscoder == nullptr) {
711
myLCPTranscoder = XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgTransService->makeNewLCPTranscoder(XERCES_CPP_NAMESPACE::XMLPlatformUtils::fgMemoryManager);
712
}
713
if (myLCPTranscoder != nullptr) {
714
XERCES_CPP_NAMESPACE::TranscodeFromStr utf8(reinterpret_cast<const XMLByte*>(utf8String.c_str()), utf8String.size(), "UTF-8");
715
return myLCPTranscoder->transcode(utf8.str());
716
}
717
} catch (XERCES_CPP_NAMESPACE::TranscodingException&) {}
718
#endif
719
return utf8String;
720
}
721
722
723
std::string
724
StringUtils::trim_left(const std::string s, const std::string& t) {
725
std::string result = s;
726
result.erase(0, s.find_first_not_of(t));
727
return result;
728
}
729
730
std::string
731
StringUtils::trim_right(const std::string s, const std::string& t) {
732
std::string result = s;
733
result.erase(s.find_last_not_of(t) + 1);
734
return result;
735
}
736
737
std::string
738
StringUtils::trim(const std::string s, const std::string& t) {
739
return trim_right(trim_left(s, t), t);
740
}
741
742
743
std::string
744
StringUtils::wrapText(const std::string s, int width) {
745
std::vector<std::string> parts = StringTokenizer(s).getVector();
746
std::string result;
747
std::string line;
748
bool firstLine = true;
749
bool firstWord = true;
750
for (std::string p : parts) {
751
if ((int)(line.size() + p.size()) < width || firstWord) {
752
if (firstWord) {
753
firstWord = false;
754
} else {
755
line += " ";
756
}
757
line = line + p;
758
} else {
759
if (firstLine) {
760
firstLine = false;
761
} else {
762
result += "\n";
763
}
764
result = result + line;
765
line.clear();
766
firstWord = true;
767
}
768
}
769
if (line.size() > 0) {
770
if (firstLine) {
771
firstLine = false;
772
} else {
773
result += "\n";
774
}
775
result = result + line;
776
}
777
return result;
778
}
779
780
781
void
782
StringUtils::resetTranscoder() {
783
myLCPTranscoder = nullptr;
784
}
785
786
/****************************************************************************/
787
788