1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef FOUNDATION_ACE_FRAMEWORKS_BASE_UTILS_STRING_UTILS_H
17 #define FOUNDATION_ACE_FRAMEWORKS_BASE_UTILS_STRING_UTILS_H
18
19 #include <climits>
20 #include <cmath>
21 #include <codecvt>
22 #include <cstring>
23 #include <locale>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27
28 #include "base/geometry/calc_dimension.h"
29 #include "base/geometry/dimension.h"
30 #include "base/utils/linear_map.h"
31 #include "base/utils/utils.h"
32
33 namespace OHOS::Ace::StringUtils {
34
35 ACE_EXPORT extern const char DEFAULT_STRING[];
36 ACE_EXPORT extern const std::wstring DEFAULT_WSTRING;
37 ACE_EXPORT extern const std::u16string DEFAULT_USTRING;
38 constexpr int32_t TEXT_CASE_LOWERCASE = 1;
39 constexpr int32_t TEXT_CASE_UPPERCASE = 2;
40 constexpr double PERCENT_VALUE = 100.0;
41 const char ELLIPSIS[] = "...";
42
Str8ToStr16(const std::string & str)43 inline std::u16string Str8ToStr16(const std::string& str)
44 {
45 if (str == DEFAULT_STRING) {
46 return DEFAULT_USTRING;
47 }
48 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert(DEFAULT_STRING, DEFAULT_USTRING);
49 std::u16string result = convert.from_bytes(str);
50 return result == DEFAULT_USTRING ? u"" : result;
51 }
52
Str16ToStr8(const std::u16string & str)53 inline std::string Str16ToStr8(const std::u16string& str)
54 {
55 if (str == DEFAULT_USTRING) {
56 return DEFAULT_STRING;
57 }
58 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert(DEFAULT_STRING);
59 std::string result = convert.to_bytes(str);
60 return result == DEFAULT_STRING ? "" : result;
61 }
62
ToWstring(const std::string & str)63 inline std::wstring ToWstring(const std::string& str)
64 {
65 if (str == DEFAULT_STRING) {
66 return DEFAULT_WSTRING;
67 }
68 #ifdef WINDOWS_PLATFORM
69 std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert(DEFAULT_STRING, DEFAULT_WSTRING);
70 #else
71 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert(DEFAULT_STRING, DEFAULT_WSTRING);
72 #endif
73 std::wstring result = convert.from_bytes(str);
74 return result == DEFAULT_WSTRING ? L"" : result;
75 }
76
IsLetterOrNumberForWchar(wchar_t chr)77 inline bool IsLetterOrNumberForWchar(wchar_t chr)
78 {
79 return (chr >= L'0' && chr <= L'9') || (chr >= L'a' && chr <= L'z') || (chr >= L'A' && chr <= L'Z');
80 }
81
ToString(const std::wstring & str)82 inline std::string ToString(const std::wstring& str)
83 {
84 if (str == DEFAULT_WSTRING) {
85 return DEFAULT_STRING;
86 }
87 #ifdef WINDOWS_PLATFORM
88 std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert(DEFAULT_STRING);
89 #else
90 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert(DEFAULT_STRING);
91 #endif
92 std::string result = convert.to_bytes(str);
93 return result == DEFAULT_STRING ? "" : result;
94 }
95
NotInUtf16Bmp(char16_t c)96 inline bool NotInUtf16Bmp(char16_t c)
97 {
98 return (c & 0xF800) == 0xD800;
99 }
100
NotInBmp(wchar_t ch)101 inline bool NotInBmp(wchar_t ch)
102 {
103 return ch >= 0xD800 && ch <= 0xDBFF;
104 }
105
IsNumber(const std::string & value)106 inline bool IsNumber(const std::string& value)
107 {
108 if (value.empty()) {
109 return false;
110 }
111 return std::all_of(value.begin(), value.end(), [](char i) { return isdigit(i); });
112 }
113
ReplaceSpace(std::string & data)114 inline void ReplaceSpace(std::string& data)
115 {
116 bool isFirstSpace = true;
117 auto iter = data.begin();
118 while (iter != data.end()) {
119 if (*iter == ' ') {
120 if (isFirstSpace) {
121 iter++;
122 isFirstSpace = false;
123 } else {
124 iter = data.erase(iter);
125 }
126 } else if (*iter == '\t') {
127 *iter = ' ';
128 } else {
129 isFirstSpace = true;
130 iter++;
131 }
132 }
133 }
134
ReplaceTabAndNewLine(std::string & data)135 inline void ReplaceTabAndNewLine(std::string& data)
136 {
137 for (auto& i : data) {
138 if (i == '\r' || i == '\n') {
139 i = ' ';
140 }
141 }
142 ReplaceSpace(data);
143 }
144
RestoreEscape(const std::string & src)145 inline std::string RestoreEscape(const std::string& src)
146 {
147 std::string res;
148 for (auto &c : src) {
149 switch (c) {
150 case '\n':
151 res += "\\n";
152 break;
153 case '\r':
154 res += "\\r";
155 break;
156 case '\t':
157 res += "\\t";
158 break;
159 default:
160 res.push_back(c);
161 break;
162 }
163 }
164 return res;
165 }
166
StringToInt(const std::string & value)167 inline int32_t StringToInt(const std::string& value)
168 {
169 errno = 0;
170 char* pEnd = nullptr;
171 int64_t result = std::strtol(value.c_str(), &pEnd, 10);
172 if (pEnd == value.c_str() || (result < INT_MIN || result > INT_MAX) || errno == ERANGE) {
173 return 0;
174 } else {
175 return result;
176 }
177 }
178
StringToLongInt(const std::string & value)179 inline int64_t StringToLongInt(const std::string& value)
180 {
181 errno = 0;
182 char* pEnd = nullptr;
183 int64_t result = std::strtoll(value.c_str(), &pEnd, 10);
184 if (pEnd == value.c_str() || errno == ERANGE) {
185 return 0;
186 } else {
187 return result;
188 }
189 }
190
191 inline uint64_t StringToLongUint(const std::string& value, uint64_t defaultErr = 0)
192 {
193 errno = 0;
194 char* pEnd = nullptr;
195 uint64_t result = std::strtoull(value.c_str(), &pEnd, 10);
196 if (pEnd == value.c_str() || errno == ERANGE) {
197 return defaultErr;
198 } else {
199 return result;
200 }
201 }
202
203 inline uint32_t StringToUint(const std::string& value, uint32_t defaultErr = 0)
204 {
205 errno = 0;
206 char* pEnd = nullptr;
207 uint64_t result = std::strtoull(value.c_str(), &pEnd, 10);
208 if (pEnd == value.c_str() || result > UINT32_MAX || errno == ERANGE) {
209 return defaultErr;
210 } else {
211 return result;
212 }
213 }
214
215 // generic string to double value method without success check
StringToDouble(const std::string & value)216 inline double StringToDouble(const std::string& value)
217 {
218 char* pEnd = nullptr;
219 errno = 0;
220 double result = std::strtod(value.c_str(), &pEnd);
221 if (pEnd == value.c_str() || errno == ERANGE) {
222 return 0.0;
223 } else {
224 return result;
225 }
226 }
227 // string to double method with success check, and support for parsing number string with percentage case
StringToDouble(const std::string & value,double & result)228 inline bool StringToDouble(const std::string& value, double& result)
229 {
230 char* pEnd = nullptr;
231 double res = std::strtod(value.c_str(), &pEnd);
232 if (pEnd == value.c_str() || errno == ERANGE) {
233 return false;
234 } else if (pEnd != nullptr) {
235 if (std::strcmp(pEnd, "%") == 0) {
236 result = res / PERCENT_VALUE;
237 return true;
238 } else if (std::strcmp(pEnd, "") == 0) {
239 result = res;
240 return true;
241 }
242 }
243 return false;
244 }
245
StringToFloat(const std::string & value)246 inline float StringToFloat(const std::string& value)
247 {
248 char* pEnd = nullptr;
249 float result = std::strtof(value.c_str(), &pEnd);
250 if (pEnd == value.c_str() || errno == ERANGE) {
251 return 0.0f;
252 } else {
253 return result;
254 }
255 }
256
257 static Dimension StringToDimensionWithUnit(const std::string& value, DimensionUnit defaultUnit = DimensionUnit::PX,
258 float defaultValue = 0.0f, bool isCalc = false)
259 {
260 errno = 0;
261 if (std::strcmp(value.c_str(), "auto") == 0) {
262 return Dimension(defaultValue, DimensionUnit::AUTO);
263 }
264 char* pEnd = nullptr;
265 double result = std::strtod(value.c_str(), &pEnd);
266 if (pEnd == value.c_str() || errno == ERANGE) {
267 return Dimension(defaultValue, defaultUnit);
268 }
269 if (pEnd != nullptr) {
270 if (std::strcmp(pEnd, "%") == 0) {
271 // Parse percent, transfer from [0, 100] to [0, 1]
272 return Dimension(result / 100.0, DimensionUnit::PERCENT);
273 }
274 if (std::strcmp(pEnd, "px") == 0) {
275 return Dimension(result, DimensionUnit::PX);
276 }
277 if (std::strcmp(pEnd, "vp") == 0) {
278 return Dimension(result, DimensionUnit::VP);
279 }
280 if (std::strcmp(pEnd, "fp") == 0) {
281 return Dimension(result, DimensionUnit::FP);
282 }
283 if (std::strcmp(pEnd, "lpx") == 0) {
284 return Dimension(result, DimensionUnit::LPX);
285 }
286 if ((std::strcmp(pEnd, "\0") == 0) && isCalc) {
287 return Dimension(result, DimensionUnit::NONE);
288 }
289 if (isCalc) {
290 return Dimension(result, DimensionUnit::INVALID);
291 }
292 }
293 return Dimension(result, defaultUnit);
294 }
295
296 inline CalcDimension StringToCalcDimension(
297 const std::string& value, bool useVp = false, DimensionUnit defaultUnit = DimensionUnit::PX)
298 {
299 if (value.find("calc") != std::string::npos) {
300 return CalcDimension(value, DimensionUnit::CALC);
301 } else {
302 if (useVp) {
303 return StringToDimensionWithUnit(value, DimensionUnit::VP);
304 }
305 return StringToDimensionWithUnit(value, defaultUnit);
306 }
307 }
308
309 inline Dimension StringToDimension(const std::string& value, bool useVp = false)
310 {
311 return StringToDimensionWithUnit(value, useVp ? DimensionUnit::VP : DimensionUnit::PX);
312 }
313
StringToDimensionWithThemeValue(const std::string & value,bool useVp,const Dimension & themeValue)314 inline Dimension StringToDimensionWithThemeValue(const std::string& value, bool useVp, const Dimension& themeValue)
315 {
316 errno = 0;
317 char* pEnd = nullptr;
318 std::strtod(value.c_str(), &pEnd);
319 if (pEnd == value.c_str() || errno == ERANGE) {
320 return themeValue;
321 }
322
323 return StringToDimensionWithUnit(value, useVp ? DimensionUnit::VP : DimensionUnit::PX);
324 }
325
326 static bool StringToDimensionWithUnitNG(const std::string& value, Dimension& dimensionResult,
327 DimensionUnit defaultUnit = DimensionUnit::PX, float defaultValue = 0.0f, bool isCalc = false)
328 {
329 errno = 0;
330 if (std::strcmp(value.c_str(), "auto") == 0) {
331 dimensionResult = Dimension(defaultValue, DimensionUnit::AUTO);
332 return true;
333 }
334 char* pEnd = nullptr;
335 double result = std::strtod(value.c_str(), &pEnd);
336 if (pEnd == value.c_str() || errno == ERANGE) {
337 dimensionResult = Dimension(defaultValue, defaultUnit);
338 return false;
339 }
340 if (pEnd != nullptr) {
341 if (std::strcmp(pEnd, "%") == 0) {
342 // Parse percent, transfer from [0, 100] to [0, 1]
343 dimensionResult = Dimension(result / 100.0, DimensionUnit::PERCENT);
344 return true;
345 }
346 if (std::strcmp(pEnd, "px") == 0) {
347 dimensionResult = Dimension(result, DimensionUnit::PX);
348 return true;
349 }
350 if (std::strcmp(pEnd, "vp") == 0) {
351 dimensionResult = Dimension(result, DimensionUnit::VP);
352 return true;
353 }
354 if (std::strcmp(pEnd, "fp") == 0) {
355 dimensionResult = Dimension(result, DimensionUnit::FP);
356 return true;
357 }
358 if (std::strcmp(pEnd, "lpx") == 0) {
359 dimensionResult = Dimension(result, DimensionUnit::LPX);
360 return true;
361 }
362 if ((std::strcmp(pEnd, "\0") == 0) && isCalc) {
363 dimensionResult = Dimension(result, DimensionUnit::NONE);
364 return true;
365 }
366 if (isCalc) {
367 dimensionResult = Dimension(result, DimensionUnit::INVALID);
368 return true;
369 }
370 if ((std::strcmp(pEnd, "\0") != 0)) {
371 dimensionResult = Dimension(result, DimensionUnit::NONE);
372 return false;
373 }
374 }
375 dimensionResult = Dimension(result, defaultUnit);
376 return true;
377 }
378
379 inline bool StringToCalcDimensionNG(
380 const std::string& value, CalcDimension& result, bool useVp = false,
381 DimensionUnit defaultUnit = DimensionUnit::PX)
382 {
383 if (value.find("calc") != std::string::npos) {
384 result = CalcDimension(value, DimensionUnit::CALC);
385 return true;
386 } else {
387 return StringToDimensionWithUnitNG(value, result, useVp ? DimensionUnit::VP : defaultUnit);
388 }
389 }
390
ReplaceChar(std::string str,char old_char,char new_char)391 inline std::string ReplaceChar(std::string str, char old_char, char new_char)
392 {
393 for (char& it : str) {
394 if (it == old_char) {
395 it = new_char;
396 }
397 }
398 return str;
399 }
400
StringToDegree(const std::string & value)401 inline double StringToDegree(const std::string& value)
402 {
403 // https://developer.mozilla.org/zh-CN/docs/Web/CSS/angle
404 constexpr static double DEGREES = 360.0;
405 constexpr static double GRADIANS = 400.0;
406 constexpr static double RADIUS = 2 * M_PI;
407
408 errno = 0;
409 char* pEnd = nullptr;
410 double result = std::strtod(value.c_str(), &pEnd);
411 if (pEnd == value.c_str() || errno == ERANGE) {
412 return 0.0;
413 } else if (pEnd) {
414 if ((std::strcmp(pEnd, "deg")) == 0) {
415 return result;
416 } else if (std::strcmp(pEnd, "grad") == 0) {
417 return result / GRADIANS * DEGREES;
418 } else if (std::strcmp(pEnd, "rad") == 0) {
419 return result / RADIUS * DEGREES;
420 } else if (std::strcmp(pEnd, "turn") == 0) {
421 return result * DEGREES;
422 }
423 }
424 return StringToDouble(value);
425 }
426
427 template<class T>
StringSplitter(const std::string & source,char delimiter,T (* func)(const std::string &),std::vector<T> & out)428 inline void StringSplitter(
429 const std::string& source, char delimiter, T (*func)(const std::string&), std::vector<T>& out)
430 {
431 out.erase(out.begin(), out.end());
432
433 if (source.empty()) {
434 return;
435 }
436
437 std::size_t startIndex = 0;
438 for (std::size_t index = 0; index < source.size(); index++) {
439 if (source[index] != delimiter) {
440 continue;
441 }
442
443 if (index > startIndex) {
444 out.emplace_back(func(source.substr(startIndex, index - startIndex)));
445 }
446 startIndex = index + 1;
447 }
448
449 if (startIndex < source.size()) {
450 out.emplace_back(func(source.substr(startIndex)));
451 }
452 }
453
StringSplitter(const std::string & source,char delimiter,std::vector<std::string> & out)454 inline void StringSplitter(const std::string& source, char delimiter, std::vector<std::string>& out)
455 {
456 using Func = std::string (*)(const std::string&);
457 Func func = [](const std::string& value) { return value; };
458 StringSplitter(source, delimiter, func, out);
459 }
460
StringSplitter(const std::string & source,char delimiter,std::vector<double> & out)461 inline void StringSplitter(const std::string& source, char delimiter, std::vector<double>& out)
462 {
463 using Func = double (*)(const std::string&);
464 Func func = [](const std::string& value) { return StringToDouble(value); };
465 StringSplitter(source, delimiter, func, out);
466 }
467
StringSplitter(const std::string & source,char delimiter,std::vector<float> & out)468 inline void StringSplitter(const std::string& source, char delimiter, std::vector<float>& out)
469 {
470 using Func = float (*)(const std::string&);
471 Func func = [](const std::string& value) { return StringToFloat(value); };
472 StringSplitter(source, delimiter, func, out);
473 }
474
StringSplitter(const std::string & source,char delimiter,std::vector<int> & out)475 inline void StringSplitter(const std::string& source, char delimiter, std::vector<int>& out)
476 {
477 using Func = int32_t (*)(const std::string&);
478 Func func = [](const std::string& value) { return StringToInt(value); };
479 StringSplitter(source, delimiter, func, out);
480 }
481
StringSplitter(const std::string & source,char delimiter,std::vector<Dimension> & out)482 inline void StringSplitter(const std::string& source, char delimiter, std::vector<Dimension>& out)
483 {
484 using Func = Dimension (*)(const std::string&);
485 Func func = [](const std::string& value) { return StringToDimension(value); };
486 StringSplitter(source, delimiter, func, out);
487 }
488
489 inline std::string DoubleToString(double value, int32_t precision = 2)
490 {
491 std::ostringstream result;
492 result.precision(precision);
493 if (NearEqual(value, Infinity<double>())) {
494 result << "Infinity";
495 } else {
496 result << std::fixed << value;
497 }
498 return result.str();
499 }
500
DeleteAllMark(std::string & str,const char mark)501 inline void DeleteAllMark(std::string& str, const char mark)
502 {
503 str.erase(std::remove(str.begin(), str.end(), mark), str.end());
504 }
505
506 inline std::string TrimStr(const std::string& str, char cTrim = ' ')
507 {
508 auto firstPos = str.find_first_not_of(cTrim);
509 if (firstPos == std::string::npos) {
510 return str;
511 }
512 auto endPos = str.find_last_not_of(cTrim);
513 return str.substr(firstPos, endPos - firstPos + 1);
514 }
515
516 inline void TrimStrLeadingAndTrailing(std::string& str, char cTrim = ' ')
517 {
518 auto firstIndexNotOfSpace = str.find_first_not_of(" ");
519 if (firstIndexNotOfSpace == std::string::npos) {
520 str = "";
521 return;
522 }
523 str.erase(0, firstIndexNotOfSpace);
524 auto lastIndexNotOfSpace = str.find_last_not_of(" ");
525 if (lastIndexNotOfSpace == std::string::npos) {
526 str = "";
527 return;
528 }
529 str.erase(lastIndexNotOfSpace + 1);
530 }
531
532 inline void SplitStr(
533 const std::string& str, const std::string& sep, std::vector<std::string>& out, bool needTrim = true)
534 {
535 out.erase(out.begin(), out.end());
536
537 if (str.empty() || sep.empty()) {
538 return;
539 }
540
541 std::string strPart;
542 std::string::size_type startPos = 0;
543 std::string::size_type pos = str.find_first_of(sep, startPos);
544 while (pos != std::string::npos) {
545 if (pos > startPos) {
546 strPart = needTrim ? TrimStr(str.substr(startPos, pos - startPos)) : str.substr(startPos, pos - startPos);
547 out.emplace_back(std::move(strPart));
548 }
549 startPos = pos + sep.size();
550 pos = str.find_first_of(sep, startPos);
551 }
552
553 if (startPos < str.size()) {
554 strPart = needTrim ? TrimStr(str.substr(startPos)) : str.substr(startPos);
555 out.emplace_back(std::move(strPart));
556 }
557 }
558
559 inline void SplitStr(const std::string& str, const std::string& sep, std::vector<Dimension>& out, bool needTrim = true)
560 {
561 out.erase(out.begin(), out.end());
562 if (str.empty() || sep.empty()) {
563 return;
564 }
565 std::string strPart;
566 std::string::size_type startPos = 0;
567 std::string::size_type pos = str.find_first_of(sep, startPos);
568 while (pos != std::string::npos) {
569 if (pos > startPos) {
570 strPart = needTrim ? TrimStr(str.substr(startPos, pos - startPos)) : str.substr(startPos, pos - startPos);
571 if (!strPart.empty()) {
572 out.emplace_back(StringToDimension(std::move(strPart)));
573 }
574 }
575 startPos = pos + sep.size();
576 pos = str.find_first_of(sep, startPos);
577 }
578 if (startPos < str.size()) {
579 strPart = needTrim ? TrimStr(str.substr(startPos)) : str.substr(startPos);
580 if (!strPart.empty()) {
581 out.emplace_back(StringToDimension(std::move(strPart)));
582 }
583 }
584 }
585
586 const std::string ACE_EXPORT FormatString(const char* fmt, ...);
587
StartWith(const std::string & dst,const std::string & prefix)588 inline bool StartWith(const std::string& dst, const std::string& prefix)
589 {
590 return dst.compare(0, prefix.size(), prefix) == 0;
591 }
592
StartWith(const std::string & str,const char * prefix,size_t prefixLen)593 inline bool StartWith(const std::string& str, const char* prefix, size_t prefixLen)
594 {
595 return ((str.length() >= prefixLen) && (str.compare(0, prefixLen, prefix) == 0));
596 }
597
EndWith(const std::string & dst,const std::string & suffix)598 inline bool EndWith(const std::string& dst, const std::string& suffix)
599 {
600 return (dst.size() >= suffix.size()) && dst.compare(dst.size() - suffix.size(), suffix.size(), suffix) == 0;
601 }
602
EndWith(const std::string & str,const char * suffix,size_t suffixLen)603 inline bool EndWith(const std::string& str, const char* suffix, size_t suffixLen)
604 {
605 size_t len = str.length();
606 return ((len >= suffixLen) && (str.compare(len - suffixLen, suffixLen, suffix) == 0));
607 }
608
TransformStrCase(std::string & str,int32_t textCase)609 inline void TransformStrCase(std::string& str, int32_t textCase)
610 {
611 if (str.empty()) {
612 return;
613 }
614
615 switch (textCase) {
616 case TEXT_CASE_LOWERCASE:
617 transform(str.begin(), str.end(), str.begin(), ::tolower);
618 break;
619 case TEXT_CASE_UPPERCASE:
620 transform(str.begin(), str.end(), str.begin(), ::toupper);
621 break;
622 default:
623 break;
624 }
625 }
626
627 bool IsAscii(const std::string& str);
628 } // namespace OHOS::Ace::StringUtils
629
630 #endif // FOUNDATION_ACE_FRAMEWORKS_BASE_UTILS_STRING_UTILS_H
631