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
ToString(const std::wstring & str)77 inline std::string ToString(const std::wstring& str)
78 {
79 if (str == DEFAULT_WSTRING) {
80 return DEFAULT_STRING;
81 }
82 #ifdef WINDOWS_PLATFORM
83 std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert(DEFAULT_STRING);
84 #else
85 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert(DEFAULT_STRING);
86 #endif
87 std::string result = convert.to_bytes(str);
88 return result == DEFAULT_STRING ? "" : result;
89 }
90
NotInUtf16Bmp(char16_t c)91 inline bool NotInUtf16Bmp(char16_t c)
92 {
93 return (c & 0xF800) == 0xD800;
94 }
95
NotInBmp(wchar_t ch)96 inline bool NotInBmp(wchar_t ch)
97 {
98 return ch >= 0xD800 && ch <= 0xDBFF;
99 }
100
IsNumber(const std::string & value)101 inline bool IsNumber(const std::string& value)
102 {
103 if (value.empty()) {
104 return false;
105 }
106 return std::all_of(value.begin(), value.end(), [](char i) { return isdigit(i); });
107 }
108
ReplaceSpace(std::string & data)109 inline void ReplaceSpace(std::string& data)
110 {
111 bool isFirstSpace = true;
112 auto iter = data.begin();
113 while (iter != data.end()) {
114 if (*iter == ' ') {
115 if (isFirstSpace) {
116 iter++;
117 isFirstSpace = false;
118 } else {
119 iter = data.erase(iter);
120 }
121 } else if (*iter == '\t') {
122 *iter = ' ';
123 } else {
124 isFirstSpace = true;
125 iter++;
126 }
127 }
128 }
129
ReplaceTabAndNewLine(std::string & data)130 inline void ReplaceTabAndNewLine(std::string& data)
131 {
132 for (auto& i : data) {
133 if (i == '\r' || i == '\n') {
134 i = ' ';
135 }
136 }
137 ReplaceSpace(data);
138 }
139
StringToInt(const std::string & value)140 inline int32_t StringToInt(const std::string& value)
141 {
142 errno = 0;
143 char* pEnd = nullptr;
144 int64_t result = std::strtol(value.c_str(), &pEnd, 10);
145 if (pEnd == value.c_str() || (result < INT_MIN || result > INT_MAX) || errno == ERANGE) {
146 return 0;
147 } else {
148 return result;
149 }
150 }
151
StringToLongInt(const std::string & value)152 inline int64_t StringToLongInt(const std::string& value)
153 {
154 errno = 0;
155 char* pEnd = nullptr;
156 int64_t result = std::strtoll(value.c_str(), &pEnd, 10);
157 if (pEnd == value.c_str() || errno == ERANGE) {
158 return 0;
159 } else {
160 return result;
161 }
162 }
163
164 inline uint32_t StringToUint(const std::string& value, uint32_t defaultErr = 0)
165 {
166 errno = 0;
167 char* pEnd = nullptr;
168 uint64_t result = std::strtoull(value.c_str(), &pEnd, 10);
169 if (pEnd == value.c_str() || result > UINT32_MAX || errno == ERANGE) {
170 return defaultErr;
171 } else {
172 return result;
173 }
174 }
175 // generic string to double value method without success check
StringToDouble(const std::string & value)176 inline double StringToDouble(const std::string& value)
177 {
178 char* pEnd = nullptr;
179 double result = std::strtod(value.c_str(), &pEnd);
180 if (pEnd == value.c_str() || errno == ERANGE) {
181 return 0.0;
182 } else {
183 return result;
184 }
185 }
186 // string to double method with success check, and support for parsing number string with percentage case
StringToDouble(const std::string & value,double & result)187 inline bool StringToDouble(const std::string& value, double& result)
188 {
189 char* pEnd = nullptr;
190 double res = std::strtod(value.c_str(), &pEnd);
191 if (pEnd == value.c_str() || errno == ERANGE) {
192 return false;
193 } else if (pEnd != nullptr) {
194 if (std::strcmp(pEnd, "%") == 0) {
195 result = res / PERCENT_VALUE;
196 return true;
197 } else if (std::strcmp(pEnd, "") == 0) {
198 result = res;
199 return true;
200 }
201 }
202 return false;
203 }
204
StringToFloat(const std::string & value)205 inline float StringToFloat(const std::string& value)
206 {
207 char* pEnd = nullptr;
208 float result = std::strtof(value.c_str(), &pEnd);
209 if (pEnd == value.c_str() || errno == ERANGE) {
210 return 0.0f;
211 } else {
212 return result;
213 }
214 }
215
216 static Dimension StringToDimensionWithUnit(const std::string& value, DimensionUnit defaultUnit = DimensionUnit::PX,
217 float defaultValue = 0.0f, bool isCalc = false)
218 {
219 errno = 0;
220 if (std::strcmp(value.c_str(), "auto") == 0) {
221 return Dimension(defaultValue, DimensionUnit::AUTO);
222 }
223 char* pEnd = nullptr;
224 double result = std::strtod(value.c_str(), &pEnd);
225 if (pEnd == value.c_str() || errno == ERANGE) {
226 return Dimension(defaultValue, defaultUnit);
227 }
228 if (pEnd != nullptr) {
229 if (std::strcmp(pEnd, "%") == 0) {
230 // Parse percent, transfer from [0, 100] to [0, 1]
231 return Dimension(result / 100.0, DimensionUnit::PERCENT);
232 }
233 if (std::strcmp(pEnd, "px") == 0) {
234 return Dimension(result, DimensionUnit::PX);
235 }
236 if (std::strcmp(pEnd, "vp") == 0) {
237 return Dimension(result, DimensionUnit::VP);
238 }
239 if (std::strcmp(pEnd, "fp") == 0) {
240 return Dimension(result, DimensionUnit::FP);
241 }
242 if (std::strcmp(pEnd, "lpx") == 0) {
243 return Dimension(result, DimensionUnit::LPX);
244 }
245 if ((std::strcmp(pEnd, "\0") == 0) && isCalc) {
246 return Dimension(result, DimensionUnit::NONE);
247 }
248 if (isCalc) {
249 return Dimension(result, DimensionUnit::INVALID);
250 }
251 }
252 return Dimension(result, defaultUnit);
253 }
254
255 inline CalcDimension StringToCalcDimension(
256 const std::string& value, bool useVp = false, DimensionUnit defaultUnit = DimensionUnit::PX)
257 {
258 if (value.find("calc") != std::string::npos) {
259 LOGI("StringToCalcDimension calc value = %{public}s", value.c_str());
260 return CalcDimension(value, DimensionUnit::CALC);
261 } else {
262 if (useVp) {
263 return StringToDimensionWithUnit(value, DimensionUnit::VP);
264 }
265 return StringToDimensionWithUnit(value, defaultUnit);
266 }
267 }
268
269 inline Dimension StringToDimension(const std::string& value, bool useVp = false)
270 {
271 return StringToDimensionWithUnit(value, useVp ? DimensionUnit::VP : DimensionUnit::PX);
272 }
273
StringToDimensionWithThemeValue(const std::string & value,bool useVp,const Dimension & themeValue)274 inline Dimension StringToDimensionWithThemeValue(const std::string& value, bool useVp, const Dimension& themeValue)
275 {
276 errno = 0;
277 char* pEnd = nullptr;
278 std::strtod(value.c_str(), &pEnd);
279 if (pEnd == value.c_str() || errno == ERANGE) {
280 return themeValue;
281 }
282
283 return StringToDimensionWithUnit(value, useVp ? DimensionUnit::VP : DimensionUnit::PX);
284 }
285
286 static bool StringToDimensionWithUnitNG(const std::string& value, Dimension& dimensionResult,
287 DimensionUnit defaultUnit = DimensionUnit::PX, float defaultValue = 0.0f, bool isCalc = false)
288 {
289 errno = 0;
290 if (std::strcmp(value.c_str(), "auto") == 0) {
291 dimensionResult = Dimension(defaultValue, DimensionUnit::AUTO);
292 return true;
293 }
294 char* pEnd = nullptr;
295 double result = std::strtod(value.c_str(), &pEnd);
296 if (pEnd == value.c_str() || errno == ERANGE) {
297 dimensionResult = Dimension(defaultValue, defaultUnit);
298 return false;
299 }
300 if (pEnd != nullptr) {
301 if (std::strcmp(pEnd, "%") == 0) {
302 // Parse percent, transfer from [0, 100] to [0, 1]
303 dimensionResult = Dimension(result / 100.0, DimensionUnit::PERCENT);
304 return true;
305 }
306 if (std::strcmp(pEnd, "px") == 0) {
307 dimensionResult = Dimension(result, DimensionUnit::PX);
308 return true;
309 }
310 if (std::strcmp(pEnd, "vp") == 0) {
311 dimensionResult = Dimension(result, DimensionUnit::VP);
312 return true;
313 }
314 if (std::strcmp(pEnd, "fp") == 0) {
315 dimensionResult = Dimension(result, DimensionUnit::FP);
316 return true;
317 }
318 if (std::strcmp(pEnd, "lpx") == 0) {
319 dimensionResult = Dimension(result, DimensionUnit::LPX);
320 return true;
321 }
322 if ((std::strcmp(pEnd, "\0") == 0) && isCalc) {
323 dimensionResult = Dimension(result, DimensionUnit::NONE);
324 return true;
325 }
326 if (isCalc) {
327 dimensionResult = Dimension(result, DimensionUnit::INVALID);
328 return true;
329 }
330 if ((std::strcmp(pEnd, "\0") != 0)) {
331 dimensionResult = Dimension(result, DimensionUnit::NONE);
332 return false;
333 }
334 }
335 dimensionResult = Dimension(result, defaultUnit);
336 return true;
337 }
338
339 inline bool StringToCalcDimensionNG(
340 const std::string& value, CalcDimension& result, bool useVp = false,
341 DimensionUnit defaultUnit = DimensionUnit::PX)
342 {
343 if (value.find("calc") != std::string::npos) {
344 result = CalcDimension(value, DimensionUnit::CALC);
345 return true;
346 } else {
347 return StringToDimensionWithUnitNG(value, result, useVp ? DimensionUnit::VP : defaultUnit);
348 }
349 }
350
StringToDegree(const std::string & value)351 inline double StringToDegree(const std::string& value)
352 {
353 // https://developer.mozilla.org/zh-CN/docs/Web/CSS/angle
354 constexpr static double DEGREES = 360.0;
355 constexpr static double GRADIANS = 400.0;
356 constexpr static double RADIUS = 2 * M_PI;
357
358 errno = 0;
359 char* pEnd = nullptr;
360 double result = std::strtod(value.c_str(), &pEnd);
361 if (pEnd == value.c_str() || errno == ERANGE) {
362 return 0.0;
363 } else if (pEnd) {
364 if ((std::strcmp(pEnd, "deg")) == 0) {
365 return result;
366 } else if (std::strcmp(pEnd, "grad") == 0) {
367 return result / GRADIANS * DEGREES;
368 } else if (std::strcmp(pEnd, "rad") == 0) {
369 return result / RADIUS * DEGREES;
370 } else if (std::strcmp(pEnd, "turn") == 0) {
371 return result * DEGREES;
372 }
373 }
374 return StringToDouble(value);
375 }
376
377 template<class T>
StringSplitter(const std::string & source,char delimiter,T (* func)(const std::string &),std::vector<T> & out)378 inline void StringSplitter(
379 const std::string& source, char delimiter, T (*func)(const std::string&), std::vector<T>& out)
380 {
381 out.erase(out.begin(), out.end());
382
383 if (source.empty()) {
384 return;
385 }
386
387 std::size_t startIndex = 0;
388 for (std::size_t index = 0; index < source.size(); index++) {
389 if (source[index] != delimiter) {
390 continue;
391 }
392
393 if (index > startIndex) {
394 out.emplace_back(func(source.substr(startIndex, index - startIndex)));
395 }
396 startIndex = index + 1;
397 }
398
399 if (startIndex < source.size()) {
400 out.emplace_back(func(source.substr(startIndex)));
401 }
402 }
403
StringSplitter(const std::string & source,char delimiter,std::vector<std::string> & out)404 inline void StringSplitter(const std::string& source, char delimiter, std::vector<std::string>& out)
405 {
406 using Func = std::string (*)(const std::string&);
407 Func func = [](const std::string& value) { return value; };
408 StringSplitter(source, delimiter, func, out);
409 }
410
StringSplitter(const std::string & source,char delimiter,std::vector<double> & out)411 inline void StringSplitter(const std::string& source, char delimiter, std::vector<double>& out)
412 {
413 using Func = double (*)(const std::string&);
414 Func func = [](const std::string& value) { return StringToDouble(value); };
415 StringSplitter(source, delimiter, func, out);
416 }
417
StringSplitter(const std::string & source,char delimiter,std::vector<float> & out)418 inline void StringSplitter(const std::string& source, char delimiter, std::vector<float>& out)
419 {
420 using Func = float (*)(const std::string&);
421 Func func = [](const std::string& value) { return StringToFloat(value); };
422 StringSplitter(source, delimiter, func, out);
423 }
424
StringSplitter(const std::string & source,char delimiter,std::vector<int> & out)425 inline void StringSplitter(const std::string& source, char delimiter, std::vector<int>& out)
426 {
427 using Func = int32_t (*)(const std::string&);
428 Func func = [](const std::string& value) { return StringToInt(value); };
429 StringSplitter(source, delimiter, func, out);
430 }
431
StringSplitter(const std::string & source,char delimiter,std::vector<Dimension> & out)432 inline void StringSplitter(const std::string& source, char delimiter, std::vector<Dimension>& out)
433 {
434 using Func = Dimension (*)(const std::string&);
435 Func func = [](const std::string& value) { return StringToDimension(value); };
436 StringSplitter(source, delimiter, func, out);
437 }
438
439 inline std::string DoubleToString(double value, int32_t precision = 2)
440 {
441 std::ostringstream result;
442 result.precision(precision);
443 if (NearEqual(value, Infinity<double>())) {
444 result << "Infinity";
445 } else {
446 result << std::fixed << value;
447 }
448 return result.str();
449 }
450
DeleteAllMark(std::string & str,const char mark)451 inline void DeleteAllMark(std::string& str, const char mark)
452 {
453 str.erase(std::remove(str.begin(), str.end(), mark), str.end());
454 }
455
456 inline std::string TrimStr(const std::string& str, char cTrim = ' ')
457 {
458 auto firstPos = str.find_first_not_of(cTrim);
459 if (firstPos == std::string::npos) {
460 return str;
461 }
462 auto endPos = str.find_last_not_of(cTrim);
463 return str.substr(firstPos, endPos - firstPos + 1);
464 }
465
466 inline void TrimStrLeadingAndTrailing(std::string& str, char cTrim = ' ')
467 {
468 auto firstIndexNotOfSpace = str.find_first_not_of(" ");
469 if (firstIndexNotOfSpace == std::string::npos) {
470 str = "";
471 return;
472 }
473 str.erase(0, firstIndexNotOfSpace);
474 auto lastIndexNotOfSpace = str.find_last_not_of(" ");
475 if (lastIndexNotOfSpace == std::string::npos) {
476 str = "";
477 return;
478 }
479 str.erase(lastIndexNotOfSpace + 1);
480 }
481
482 inline void SplitStr(
483 const std::string& str, const std::string& sep, std::vector<std::string>& out, bool needTrim = true)
484 {
485 out.erase(out.begin(), out.end());
486
487 if (str.empty() || sep.empty()) {
488 return;
489 }
490
491 std::string strPart;
492 std::string::size_type startPos = 0;
493 std::string::size_type pos = str.find_first_of(sep, startPos);
494 while (pos != std::string::npos) {
495 if (pos > startPos) {
496 strPart = needTrim ? TrimStr(str.substr(startPos, pos - startPos)) : str.substr(startPos, pos - startPos);
497 out.emplace_back(std::move(strPart));
498 }
499 startPos = pos + sep.size();
500 pos = str.find_first_of(sep, startPos);
501 }
502
503 if (startPos < str.size()) {
504 strPart = needTrim ? TrimStr(str.substr(startPos)) : str.substr(startPos);
505 out.emplace_back(std::move(strPart));
506 }
507 }
508
509 inline void SplitStr(const std::string& str, const std::string& sep, std::vector<Dimension>& out, bool needTrim = true)
510 {
511 out.erase(out.begin(), out.end());
512 if (str.empty() || sep.empty()) {
513 return;
514 }
515 std::string strPart;
516 std::string::size_type startPos = 0;
517 std::string::size_type pos = str.find_first_of(sep, startPos);
518 while (pos != std::string::npos) {
519 if (pos > startPos) {
520 strPart = needTrim ? TrimStr(str.substr(startPos, pos - startPos)) : str.substr(startPos, pos - startPos);
521 if (!strPart.empty()) {
522 out.emplace_back(StringToDimension(std::move(strPart)));
523 }
524 }
525 startPos = pos + sep.size();
526 pos = str.find_first_of(sep, startPos);
527 }
528 if (startPos < str.size()) {
529 strPart = needTrim ? TrimStr(str.substr(startPos)) : str.substr(startPos);
530 if (!strPart.empty()) {
531 out.emplace_back(StringToDimension(std::move(strPart)));
532 }
533 }
534 }
535
536 const std::string ACE_EXPORT FormatString(const char* fmt, ...);
537
StartWith(const std::string & dst,const std::string & prefix)538 inline bool StartWith(const std::string& dst, const std::string& prefix)
539 {
540 return dst.compare(0, prefix.size(), prefix) == 0;
541 }
542
StartWith(const std::string & str,const char * prefix,size_t prefixLen)543 inline bool StartWith(const std::string& str, const char* prefix, size_t prefixLen)
544 {
545 return ((str.length() >= prefixLen) && (str.compare(0, prefixLen, prefix) == 0));
546 }
547
EndWith(const std::string & dst,const std::string & suffix)548 inline bool EndWith(const std::string& dst, const std::string& suffix)
549 {
550 return (dst.size() >= suffix.size()) && dst.compare(dst.size() - suffix.size(), suffix.size(), suffix) == 0;
551 }
552
EndWith(const std::string & str,const char * suffix,size_t suffixLen)553 inline bool EndWith(const std::string& str, const char* suffix, size_t suffixLen)
554 {
555 size_t len = str.length();
556 return ((len >= suffixLen) && (str.compare(len - suffixLen, suffixLen, suffix) == 0));
557 }
558
TransformStrCase(std::string & str,int32_t textCase)559 inline void TransformStrCase(std::string& str, int32_t textCase)
560 {
561 if (str.empty()) {
562 return;
563 }
564
565 switch (textCase) {
566 case TEXT_CASE_LOWERCASE:
567 transform(str.begin(), str.end(), str.begin(), ::tolower);
568 break;
569 case TEXT_CASE_UPPERCASE:
570 transform(str.begin(), str.end(), str.begin(), ::toupper);
571 break;
572 default:
573 break;
574 }
575 }
576
577 bool IsAscii(const std::string& str);
578 } // namespace OHOS::Ace::StringUtils
579
580 #endif // FOUNDATION_ACE_FRAMEWORKS_BASE_UTILS_STRING_UTILS_H
581