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 inline Dimension StringToDimensionWithUnit(
217 const std::string& value, DimensionUnit defaultUnit = DimensionUnit::PX, float defaultValue = 0.0f)
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 ((pEnd) && (std::strcmp(pEnd, "lpx") == 0)) {
243 return Dimension(result, DimensionUnit::LPX);
244 }
245 }
246 return Dimension(result, defaultUnit);
247 }
248
249 inline CalcDimension StringToCalcDimension(const std::string& value, bool useVp = false)
250 {
251 if (value.find("calc") != std::string::npos) {
252 return CalcDimension(value, DimensionUnit::CALC);
253 } else {
254 return StringToDimensionWithUnit(value, useVp ? DimensionUnit::VP : DimensionUnit::PX);
255 }
256 }
257
258 inline Dimension StringToDimension(const std::string& value, bool useVp = false)
259 {
260 return StringToDimensionWithUnit(value, useVp ? DimensionUnit::VP : DimensionUnit::PX);
261 }
262
StringToDimensionWithThemeValue(const std::string & value,bool useVp,const Dimension & themeValue)263 inline Dimension StringToDimensionWithThemeValue(const std::string& value, bool useVp, const Dimension& themeValue)
264 {
265 errno = 0;
266 char* pEnd = nullptr;
267 std::strtod(value.c_str(), &pEnd);
268 if (pEnd == value.c_str() || errno == ERANGE) {
269 return themeValue;
270 }
271
272 return StringToDimensionWithUnit(value, useVp ? DimensionUnit::VP : DimensionUnit::PX);
273 }
274
StringToDegree(const std::string & value)275 inline double StringToDegree(const std::string& value)
276 {
277 // https://developer.mozilla.org/zh-CN/docs/Web/CSS/angle
278 constexpr static double DEGREES = 360.0;
279 constexpr static double GRADIANS = 400.0;
280 constexpr static double RADIUS = 2 * M_PI;
281
282 errno = 0;
283 char* pEnd = nullptr;
284 double result = std::strtod(value.c_str(), &pEnd);
285 if (pEnd == value.c_str() || errno == ERANGE) {
286 return 0.0;
287 } else if (pEnd) {
288 if ((std::strcmp(pEnd, "deg")) == 0) {
289 return result;
290 } else if (std::strcmp(pEnd, "grad") == 0) {
291 return result / GRADIANS * DEGREES;
292 } else if (std::strcmp(pEnd, "rad") == 0) {
293 return result / RADIUS * DEGREES;
294 } else if (std::strcmp(pEnd, "turn") == 0) {
295 return result * DEGREES;
296 }
297 }
298 return StringToDouble(value);
299 }
300
301 template<class T>
StringSplitter(const std::string & source,char delimiter,T (* func)(const std::string &),std::vector<T> & out)302 inline void StringSplitter(
303 const std::string& source, char delimiter, T (*func)(const std::string&), std::vector<T>& out)
304 {
305 out.erase(out.begin(), out.end());
306
307 if (source.empty()) {
308 return;
309 }
310
311 std::size_t startIndex = 0;
312 for (std::size_t index = 0; index < source.size(); index++) {
313 if (source[index] != delimiter) {
314 continue;
315 }
316
317 if (index > startIndex) {
318 out.emplace_back(func(source.substr(startIndex, index - startIndex)));
319 }
320 startIndex = index + 1;
321 }
322
323 if (startIndex < source.size()) {
324 out.emplace_back(func(source.substr(startIndex)));
325 }
326 }
327
StringSplitter(const std::string & source,char delimiter,std::vector<std::string> & out)328 inline void StringSplitter(const std::string& source, char delimiter, std::vector<std::string>& out)
329 {
330 using Func = std::string (*)(const std::string&);
331 Func func = [](const std::string& value) { return value; };
332 StringSplitter(source, delimiter, func, out);
333 }
334
StringSplitter(const std::string & source,char delimiter,std::vector<double> & out)335 inline void StringSplitter(const std::string& source, char delimiter, std::vector<double>& out)
336 {
337 using Func = double (*)(const std::string&);
338 Func func = [](const std::string& value) { return StringToDouble(value); };
339 StringSplitter(source, delimiter, func, out);
340 }
341
StringSplitter(const std::string & source,char delimiter,std::vector<float> & out)342 inline void StringSplitter(const std::string& source, char delimiter, std::vector<float>& out)
343 {
344 using Func = float (*)(const std::string&);
345 Func func = [](const std::string& value) { return StringToFloat(value); };
346 StringSplitter(source, delimiter, func, out);
347 }
348
StringSplitter(const std::string & source,char delimiter,std::vector<int> & out)349 inline void StringSplitter(const std::string& source, char delimiter, std::vector<int>& out)
350 {
351 using Func = int32_t (*)(const std::string&);
352 Func func = [](const std::string& value) { return StringToInt(value); };
353 StringSplitter(source, delimiter, func, out);
354 }
355
StringSplitter(const std::string & source,char delimiter,std::vector<Dimension> & out)356 inline void StringSplitter(const std::string& source, char delimiter, std::vector<Dimension>& out)
357 {
358 using Func = Dimension (*)(const std::string&);
359 Func func = [](const std::string& value) { return StringToDimension(value); };
360 StringSplitter(source, delimiter, func, out);
361 }
362
363 inline std::string DoubleToString(double value, int32_t precision = 2)
364 {
365 std::ostringstream result;
366 result.precision(precision);
367 if (NearEqual(value, Infinity<double>())) {
368 result << "Infinity";
369 } else {
370 result << std::fixed << value;
371 }
372 return result.str();
373 }
374
DeleteAllMark(std::string & str,const char mark)375 inline void DeleteAllMark(std::string& str, const char mark)
376 {
377 str.erase(std::remove(str.begin(), str.end(), mark), str.end());
378 }
379
380 inline std::string TrimStr(const std::string& str, char cTrim = ' ')
381 {
382 auto firstPos = str.find_first_not_of(cTrim);
383 if (firstPos == std::string::npos) {
384 return str;
385 }
386 auto endPos = str.find_last_not_of(cTrim);
387 return str.substr(firstPos, endPos - firstPos + 1);
388 }
389
390 inline void TrimStrLeadingAndTrailing(std::string& str, char cTrim = ' ')
391 {
392 auto firstIndexNotOfSpace = str.find_first_not_of(" ");
393 if (firstIndexNotOfSpace == std::string::npos) {
394 str = "";
395 return;
396 }
397 str.erase(0, firstIndexNotOfSpace);
398 auto lastIndexNotOfSpace = str.find_last_not_of(" ");
399 if (lastIndexNotOfSpace == std::string::npos) {
400 str = "";
401 return;
402 }
403 str.erase(lastIndexNotOfSpace + 1);
404 }
405
406 inline void SplitStr(
407 const std::string& str, const std::string& sep, std::vector<std::string>& out, bool needTrim = true)
408 {
409 out.erase(out.begin(), out.end());
410
411 if (str.empty() || sep.empty()) {
412 return;
413 }
414
415 std::string strPart;
416 std::string::size_type startPos = 0;
417 std::string::size_type pos = str.find_first_of(sep, startPos);
418 while (pos != std::string::npos) {
419 if (pos > startPos) {
420 strPart = needTrim ? TrimStr(str.substr(startPos, pos - startPos)) : str.substr(startPos, pos - startPos);
421 out.emplace_back(std::move(strPart));
422 }
423 startPos = pos + sep.size();
424 pos = str.find_first_of(sep, startPos);
425 }
426
427 if (startPos < str.size()) {
428 strPart = needTrim ? TrimStr(str.substr(startPos)) : str.substr(startPos);
429 out.emplace_back(std::move(strPart));
430 }
431 }
432
433 inline void SplitStr(const std::string& str, const std::string& sep, std::vector<Dimension>& out, bool needTrim = true)
434 {
435 out.erase(out.begin(), out.end());
436 if (str.empty() || sep.empty()) {
437 return;
438 }
439 std::string strPart;
440 std::string::size_type startPos = 0;
441 std::string::size_type pos = str.find_first_of(sep, startPos);
442 while (pos != std::string::npos) {
443 if (pos > startPos) {
444 strPart = needTrim ? TrimStr(str.substr(startPos, pos - startPos)) : str.substr(startPos, pos - startPos);
445 if (!strPart.empty()) {
446 out.emplace_back(StringToDimension(std::move(strPart)));
447 }
448 }
449 startPos = pos + sep.size();
450 pos = str.find_first_of(sep, startPos);
451 }
452 if (startPos < str.size()) {
453 strPart = needTrim ? TrimStr(str.substr(startPos)) : str.substr(startPos);
454 if (!strPart.empty()) {
455 out.emplace_back(StringToDimension(std::move(strPart)));
456 }
457 }
458 }
459
460 const std::string ACE_EXPORT FormatString(const char* fmt, ...);
461
StartWith(const std::string & dst,const std::string & prefix)462 inline bool StartWith(const std::string& dst, const std::string& prefix)
463 {
464 return dst.compare(0, prefix.size(), prefix) == 0;
465 }
466
StartWith(const std::string & str,const char * prefix,size_t prefixLen)467 inline bool StartWith(const std::string& str, const char* prefix, size_t prefixLen)
468 {
469 return ((str.length() >= prefixLen) && (str.compare(0, prefixLen, prefix) == 0));
470 }
471
EndWith(const std::string & dst,const std::string & suffix)472 inline bool EndWith(const std::string& dst, const std::string& suffix)
473 {
474 return (dst.size() >= suffix.size()) && dst.compare(dst.size() - suffix.size(), suffix.size(), suffix) == 0;
475 }
476
EndWith(const std::string & str,const char * suffix,size_t suffixLen)477 inline bool EndWith(const std::string& str, const char* suffix, size_t suffixLen)
478 {
479 size_t len = str.length();
480 return ((len >= suffixLen) && (str.compare(len - suffixLen, suffixLen, suffix) == 0));
481 }
482
TransformStrCase(std::string & str,int32_t textCase)483 inline void TransformStrCase(std::string& str, int32_t textCase)
484 {
485 if (str.empty()) {
486 return;
487 }
488
489 switch (textCase) {
490 case TEXT_CASE_LOWERCASE:
491 transform(str.begin(), str.end(), str.begin(), ::tolower);
492 break;
493 case TEXT_CASE_UPPERCASE:
494 transform(str.begin(), str.end(), str.begin(), ::toupper);
495 break;
496 default:
497 break;
498 }
499 }
500
501 } // namespace OHOS::Ace::StringUtils
502
503 #endif // FOUNDATION_ACE_FRAMEWORKS_BASE_UTILS_STRING_UTILS_H
504