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
33namespace OHOS::Ace::StringUtils {
34
35ACE_FORCE_EXPORT  extern const char DEFAULT_STRING[];
36ACE_EXPORT extern const std::wstring DEFAULT_WSTRING;
37ACE_FORCE_EXPORT  extern const std::u16string DEFAULT_USTRING;
38ACE_EXPORT extern const std::u32string DEFAULT_U32STRING;
39constexpr int32_t TEXT_CASE_LOWERCASE = 1;
40constexpr int32_t TEXT_CASE_UPPERCASE = 2;
41constexpr double PERCENT_VALUE = 100.0;
42constexpr double DEGREES_VALUE = 360.0; // one turn means 360 deg
43constexpr double GRADIANS_VALUE = 400.0; // one turn means 400 grad
44constexpr double RADIANS_VALUE = 2 * M_PI; // one turn means 2*pi rad
45const char ELLIPSIS[] = "...";
46
47inline std::u16string Str8ToStr16(const std::string& str)
48{
49    if (str == DEFAULT_STRING) {
50        return DEFAULT_USTRING;
51    }
52    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert(DEFAULT_STRING, DEFAULT_USTRING);
53    std::u16string result = convert.from_bytes(str);
54    return result == DEFAULT_USTRING ? u"" : result;
55}
56
57inline std::string Str16ToStr8(const std::u16string& str)
58{
59    if (str == DEFAULT_USTRING) {
60        return DEFAULT_STRING;
61    }
62    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert(DEFAULT_STRING);
63    std::string result = convert.to_bytes(str);
64    return result == DEFAULT_STRING ? "" : result;
65}
66
67inline std::wstring ToWstring(const std::string& str)
68{
69    if (str == DEFAULT_STRING) {
70        return DEFAULT_WSTRING;
71    }
72#ifdef PREVIEW
73    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert(DEFAULT_STRING, DEFAULT_WSTRING);
74#elif WINDOWS_PLATFORM
75    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert(DEFAULT_STRING, DEFAULT_WSTRING);
76#else
77    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert(DEFAULT_STRING, DEFAULT_WSTRING);
78#endif
79    std::wstring result = convert.from_bytes(str);
80    return result == DEFAULT_WSTRING ? L"" : result;
81}
82
83inline bool IsLetterOrNumberForWchar(wchar_t chr)
84{
85    return (chr >= L'0' && chr <= L'9') || (chr >= L'a' && chr <= L'z') || (chr >= L'A' && chr <= L'Z');
86}
87
88inline std::string ToString(const std::wstring& str)
89{
90    if (str == DEFAULT_WSTRING) {
91        return DEFAULT_STRING;
92    }
93#ifdef PREVIEW
94    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert(DEFAULT_STRING);
95#elif WINDOWS_PLATFORM
96    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert(DEFAULT_STRING);
97#else
98    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert(DEFAULT_STRING);
99#endif
100    std::string result = convert.to_bytes(str);
101    return result == DEFAULT_STRING ? "" : result;
102}
103
104inline std::u32string ToU32string(const std::string& str)
105{
106    if (str == DEFAULT_STRING) {
107        return DEFAULT_U32STRING;
108    }
109    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert(DEFAULT_STRING, DEFAULT_U32STRING);
110    std::u32string result = convert.from_bytes(str);
111    return result == DEFAULT_U32STRING ? U"" : result;
112}
113
114inline std::string U32StringToString(const std::u32string& str)
115{
116    if (str == DEFAULT_U32STRING) {
117        return DEFAULT_STRING;
118    }
119    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert(DEFAULT_STRING);
120    std::string result = convert.to_bytes(str);
121    return result == DEFAULT_STRING ? "" : result;
122}
123
124inline bool NotInUtf16Bmp(char16_t c)
125{
126    return (c & 0xF800) == 0xD800;
127}
128
129inline bool NotInBmp(wchar_t ch)
130{
131    return ch >= 0xD800 && ch <= 0xDBFF;
132}
133
134inline bool IsNumber(const std::string& value)
135{
136    if (value.empty()) {
137        return false;
138    }
139    return std::all_of(value.begin(), value.end(), [](char i) { return isdigit(i); });
140}
141
142inline void ReplaceSpace(std::string& data)
143{
144    bool isFirstSpace = true;
145    auto iter = data.begin();
146    while (iter != data.end()) {
147        if (*iter == ' ') {
148            if (isFirstSpace) {
149                iter++;
150                isFirstSpace = false;
151            } else {
152                iter = data.erase(iter);
153            }
154        } else if (*iter == '\t') {
155            *iter = ' ';
156        } else {
157            isFirstSpace = true;
158            iter++;
159        }
160    }
161}
162
163inline void ReplaceTabAndNewLine(std::string& data)
164{
165    for (auto& i : data) {
166        if (i == '\r' || i == '\n') {
167            i = ' ';
168        }
169    }
170    ReplaceSpace(data);
171}
172
173inline std::string RestoreEscape(const std::string& src)
174{
175    std::string res;
176    for (auto &c : src) {
177        switch (c) {
178            case '\n':
179                res += "\\n";
180                break;
181            case '\r':
182                res += "\\r";
183                break;
184            case '\t':
185                res += "\\t";
186                break;
187            default:
188                res.push_back(c);
189                break;
190        }
191    }
192    return res;
193}
194
195inline std::string RestoreBackslash(const std::string& src)
196{
197    std::string res;
198    for (auto &c : src) {
199        if (c != '\\') {
200            res.push_back(c);
201        }
202    }
203    return res;
204}
205
206inline int32_t StringToInt(const std::string& value)
207{
208    errno = 0;
209    char* pEnd = nullptr;
210    int64_t result = std::strtol(value.c_str(), &pEnd, 10);
211    if (pEnd == value.c_str() || (result < INT_MIN || result > INT_MAX) || errno == ERANGE) {
212        return 0;
213    } else {
214        return result;
215    }
216}
217
218inline int64_t StringToLongInt(const std::string& value, int64_t defaultErr = 0)
219{
220    errno = 0;
221    char* pEnd = nullptr;
222    int64_t result = std::strtoll(value.c_str(), &pEnd, 10);
223    if (pEnd == value.c_str() || errno == ERANGE) {
224        return defaultErr;
225    } else {
226        return result;
227    }
228}
229
230inline uint64_t StringToLongUint(const std::string& value, uint64_t defaultErr = 0)
231{
232    errno = 0;
233    char* pEnd = nullptr;
234    uint64_t result = std::strtoull(value.c_str(), &pEnd, 10);
235    if (pEnd == value.c_str() || errno == ERANGE) {
236        return defaultErr;
237    } else {
238        return result;
239    }
240}
241
242inline uint32_t StringToUintCheck(const std::string& value, uint32_t defaultErr = 0)
243{
244    errno = 0;
245    char* pEnd = nullptr;
246    uint64_t result = std::strtoull(value.c_str(), &pEnd, 10);
247    if ((pEnd == value.c_str()) || ((pEnd != nullptr) && (*pEnd != '\0')) || result > UINT32_MAX || errno == ERANGE) {
248        return defaultErr;
249    } else {
250        return result;
251    }
252}
253
254inline uint32_t StringToUint(const std::string& value, uint32_t defaultErr = 0)
255{
256    errno = 0;
257    char* pEnd = nullptr;
258    uint64_t result = std::strtoull(value.c_str(), &pEnd, 10);
259    if (pEnd == value.c_str() || result > UINT32_MAX || errno == ERANGE) {
260        return defaultErr;
261    } else {
262        return result;
263    }
264}
265
266// generic string to double value method without success check
267inline double StringToDouble(const std::string& value)
268{
269    char* pEnd = nullptr;
270    errno = 0;
271    double result = std::strtod(value.c_str(), &pEnd);
272    if (pEnd == value.c_str() || errno == ERANGE) {
273        return 0.0;
274    } else {
275        return result;
276    }
277}
278// string to double method with success check, and support for parsing number string with percentage case
279inline bool StringToDouble(const std::string& value, double& result)
280{
281    errno = 0;
282    char* pEnd = nullptr;
283    double res = std::strtod(value.c_str(), &pEnd);
284    if (pEnd == value.c_str() || errno == ERANGE) {
285        return false;
286    } else if (pEnd != nullptr) {
287        if (std::strcmp(pEnd, "%") == 0) {
288            result = res / PERCENT_VALUE;
289            return true;
290        } else if (std::strcmp(pEnd, "") == 0) {
291            result = res;
292            return true;
293        }
294    }
295    return false;
296}
297
298inline float StringToFloat(const std::string& value)
299{
300    errno = 0;
301    char* pEnd = nullptr;
302    float result = std::strtof(value.c_str(), &pEnd);
303    if (pEnd == value.c_str() || errno == ERANGE) {
304        return 0.0f;
305    } else {
306        return result;
307    }
308}
309
310static Dimension StringToDimensionWithUnit(const std::string& value, DimensionUnit defaultUnit = DimensionUnit::PX,
311    float defaultValue = 0.0f, bool isCalc = false)
312{
313    errno = 0;
314    if (std::strcmp(value.c_str(), "auto") == 0) {
315        return Dimension(defaultValue, DimensionUnit::AUTO);
316    }
317    char* pEnd = nullptr;
318    double result = std::strtod(value.c_str(), &pEnd);
319    if (pEnd == value.c_str() || errno == ERANGE) {
320        return Dimension(defaultValue, defaultUnit);
321    }
322    if (pEnd != nullptr) {
323        if (std::strcmp(pEnd, "%") == 0) {
324            // Parse percent, transfer from [0, 100] to [0, 1]
325            return Dimension(result / 100.0, DimensionUnit::PERCENT);
326        }
327        if (std::strcmp(pEnd, "px") == 0) {
328            return Dimension(result, DimensionUnit::PX);
329        }
330        if (std::strcmp(pEnd, "vp") == 0) {
331            return Dimension(result, DimensionUnit::VP);
332        }
333        if (std::strcmp(pEnd, "fp") == 0) {
334            return Dimension(result, DimensionUnit::FP);
335        }
336        if (std::strcmp(pEnd, "lpx") == 0) {
337            return Dimension(result, DimensionUnit::LPX);
338        }
339        if ((std::strcmp(pEnd, "\0") == 0) && isCalc) {
340            return Dimension(result, DimensionUnit::NONE);
341        }
342        if (isCalc) {
343            return Dimension(result, DimensionUnit::INVALID);
344        }
345    }
346    return Dimension(result, defaultUnit);
347}
348
349inline CalcDimension StringToCalcDimension(
350    const std::string& value, bool useVp = false, DimensionUnit defaultUnit = DimensionUnit::PX)
351{
352    if (value.find("calc") != std::string::npos) {
353        return CalcDimension(value, DimensionUnit::CALC);
354    } else {
355        if (useVp) {
356            return StringToDimensionWithUnit(value, DimensionUnit::VP);
357        }
358        return StringToDimensionWithUnit(value, defaultUnit);
359    }
360}
361
362inline Dimension StringToDimension(const std::string& value, bool useVp = false)
363{
364    return StringToDimensionWithUnit(value, useVp ? DimensionUnit::VP : DimensionUnit::PX);
365}
366
367inline Dimension StringToDimensionWithThemeValue(const std::string& value, bool useVp, const Dimension& themeValue)
368{
369    errno = 0;
370    char* pEnd = nullptr;
371    std::strtod(value.c_str(), &pEnd);
372    if (pEnd == value.c_str() || errno == ERANGE) {
373        return themeValue;
374    }
375
376    return StringToDimensionWithUnit(value, useVp ? DimensionUnit::VP : DimensionUnit::PX);
377}
378
379static bool StringToDimensionWithUnitNG(const std::string& value, Dimension& dimensionResult,
380    DimensionUnit defaultUnit = DimensionUnit::PX, float defaultValue = 0.0f, bool isCalc = false)
381{
382    errno = 0;
383    if (std::strcmp(value.c_str(), "auto") == 0) {
384        dimensionResult = Dimension(defaultValue, DimensionUnit::AUTO);
385        return true;
386    }
387    char* pEnd = nullptr;
388    double result = std::strtod(value.c_str(), &pEnd);
389    if (pEnd == value.c_str() || errno == ERANGE) {
390        dimensionResult = Dimension(defaultValue, defaultUnit);
391        return false;
392    }
393    if (pEnd != nullptr) {
394        if (std::strcmp(pEnd, "%") == 0) {
395            // Parse percent, transfer from [0, 100] to [0, 1]
396            dimensionResult = Dimension(result / 100.0, DimensionUnit::PERCENT);
397            return true;
398        }
399        if (std::strcmp(pEnd, "px") == 0) {
400            dimensionResult = Dimension(result, DimensionUnit::PX);
401            return true;
402        }
403        if (std::strcmp(pEnd, "vp") == 0) {
404            dimensionResult = Dimension(result, DimensionUnit::VP);
405            return true;
406        }
407        if (std::strcmp(pEnd, "fp") == 0) {
408            dimensionResult = Dimension(result, DimensionUnit::FP);
409            return true;
410        }
411        if (std::strcmp(pEnd, "lpx") == 0) {
412            dimensionResult = Dimension(result, DimensionUnit::LPX);
413            return true;
414        }
415        if ((std::strcmp(pEnd, "\0") == 0) && isCalc) {
416            dimensionResult = Dimension(result, DimensionUnit::NONE);
417            return true;
418        }
419        if (isCalc) {
420            dimensionResult = Dimension(result, DimensionUnit::INVALID);
421            return true;
422        }
423        if ((std::strcmp(pEnd, "\0") != 0)) {
424            dimensionResult = Dimension(result, DimensionUnit::NONE);
425            return false;
426        }
427    }
428    dimensionResult = Dimension(result, defaultUnit);
429    return true;
430}
431
432inline bool StringToCalcDimensionNG(
433    const std::string& value, CalcDimension& result, bool useVp = false,
434    DimensionUnit defaultUnit = DimensionUnit::PX)
435{
436    if (value.find("calc") != std::string::npos) {
437        result = CalcDimension(value, DimensionUnit::CALC);
438        return true;
439    } else {
440        return StringToDimensionWithUnitNG(value, result, useVp ? DimensionUnit::VP : defaultUnit);
441    }
442}
443
444inline std::string ReplaceChar(std::string str, char old_char, char new_char)
445{
446    for (char& it : str) {
447        if (it == old_char) {
448            it = new_char;
449        }
450    }
451    return str;
452}
453
454inline double StringToDegree(const std::string& value)
455{
456    // https://developer.mozilla.org/zh-CN/docs/Web/CSS/angle
457
458    errno = 0;
459    char* pEnd = nullptr;
460    double result = std::strtod(value.c_str(), &pEnd);
461    if (pEnd == value.c_str() || errno == ERANGE) {
462        return 0.0;
463    } else if (pEnd) {
464        if ((std::strcmp(pEnd, "deg")) == 0) {
465            return result;
466        } else if (std::strcmp(pEnd, "grad") == 0) {
467            return result / GRADIANS_VALUE * DEGREES_VALUE;
468        } else if (std::strcmp(pEnd, "rad") == 0) {
469            return result / RADIANS_VALUE * DEGREES_VALUE;
470        } else if (std::strcmp(pEnd, "turn") == 0) {
471            return result * DEGREES_VALUE;
472        }
473    }
474    return StringToDouble(value);
475}
476
477// StringToDegree with check. If the string is valid, change result and return true, otherwise return false.
478inline bool StringToDegree(const std::string& value, double& result)
479{
480    errno = 0;
481    char* pEnd = nullptr;
482    double temp = std::strtod(value.c_str(), &pEnd);
483    if (pEnd == value.c_str() || errno == ERANGE) {
484        return false;
485    } else if (pEnd) {
486        if (*pEnd == '\0') {
487            result = temp;
488            return true;
489        }
490        if (std::strcmp(pEnd, "deg") == 0) {
491            result = temp;
492            return true;
493        }
494        if (std::strcmp(pEnd, "grad") == 0) {
495            result = temp / GRADIANS_VALUE * DEGREES_VALUE;
496            return true;
497        }
498        if (std::strcmp(pEnd, "rad") == 0) {
499            result = temp / RADIANS_VALUE * DEGREES_VALUE;
500            return true;
501        }
502        if (std::strcmp(pEnd, "turn") == 0) {
503            result = temp * DEGREES_VALUE;
504            return true;
505        }
506    }
507    return false;
508}
509
510template<class T>
511inline void StringSplitter(
512    const std::string& source, char delimiter, T (*func)(const std::string&), std::vector<T>& out)
513{
514    out.erase(out.begin(), out.end());
515
516    if (source.empty()) {
517        return;
518    }
519
520    std::size_t startIndex = 0;
521    for (std::size_t index = 0; index < source.size(); index++) {
522        if (source[index] != delimiter) {
523            continue;
524        }
525
526        if (index > startIndex) {
527            out.emplace_back(func(source.substr(startIndex, index - startIndex)));
528        }
529        startIndex = index + 1;
530    }
531
532    if (startIndex < source.size()) {
533        out.emplace_back(func(source.substr(startIndex)));
534    }
535}
536
537inline bool ParseStringToArray(const std::string& input, std::vector<float>& output)
538{
539    std::istringstream iss(StringUtils::ReplaceChar(input, ',', ' '));
540    std::string token;
541
542    while (iss >> token) {
543        double value;
544        if (!StringToDouble(token, value)) {
545            return false;
546        }
547        output.emplace_back(value);
548    }
549
550    return true;
551}
552
553inline void StringSplitter(const std::string& source, char delimiter, std::vector<std::string>& out)
554{
555    using Func = std::string (*)(const std::string&);
556    Func func = [](const std::string& value) { return value; };
557    StringSplitter(source, delimiter, func, out);
558}
559
560inline void StringSplitter(const std::string& source, char delimiter, std::vector<double>& out)
561{
562    using Func = double (*)(const std::string&);
563    Func func = [](const std::string& value) { return StringToDouble(value); };
564    StringSplitter(source, delimiter, func, out);
565}
566
567inline void StringSplitter(const std::string& source, char delimiter, std::vector<float>& out)
568{
569    using Func = float (*)(const std::string&);
570    Func func = [](const std::string& value) { return StringToFloat(value); };
571    StringSplitter(source, delimiter, func, out);
572}
573
574inline void StringSplitter(const std::string& source, char delimiter, std::vector<int>& out)
575{
576    using Func = int32_t (*)(const std::string&);
577    Func func = [](const std::string& value) { return StringToInt(value); };
578    StringSplitter(source, delimiter, func, out);
579}
580
581inline void StringSplitter(const std::string& source, char delimiter, std::vector<Dimension>& out)
582{
583    using Func = Dimension (*)(const std::string&);
584    Func func = [](const std::string& value) { return StringToDimension(value); };
585    StringSplitter(source, delimiter, func, out);
586}
587
588inline std::string DoubleToString(double value, int32_t precision = 2)
589{
590    std::ostringstream result;
591    result.precision(precision);
592    if (NearEqual(value, Infinity<double>())) {
593        result << "Infinity";
594    } else {
595        result << std::fixed << value;
596    }
597    return result.str();
598}
599
600inline void DeleteAllMark(std::string& str, const char mark)
601{
602    str.erase(std::remove(str.begin(), str.end(), mark), str.end());
603}
604
605inline std::string TrimStr(const std::string& str, char cTrim = ' ')
606{
607    auto firstPos = str.find_first_not_of(cTrim);
608    if (firstPos == std::string::npos) {
609        return str;
610    }
611    auto endPos = str.find_last_not_of(cTrim);
612    return str.substr(firstPos, endPos - firstPos + 1);
613}
614
615inline void TrimStrLeadingAndTrailing(std::string& str, char cTrim = ' ')
616{
617    auto firstIndexNotOfSpace = str.find_first_not_of(" ");
618    if (firstIndexNotOfSpace == std::string::npos) {
619        str = "";
620        return;
621    }
622    str.erase(0, firstIndexNotOfSpace);
623    auto lastIndexNotOfSpace = str.find_last_not_of(" ");
624    if (lastIndexNotOfSpace == std::string::npos) {
625        str = "";
626        return;
627    }
628    str.erase(lastIndexNotOfSpace + 1);
629}
630
631inline void SplitStr(
632    const std::string& str, const std::string& sep, std::vector<std::string>& out, bool needTrim = true)
633{
634    out.erase(out.begin(), out.end());
635
636    if (str.empty() || sep.empty()) {
637        return;
638    }
639
640    std::string strPart;
641    std::string::size_type startPos = 0;
642    std::string::size_type pos = str.find_first_of(sep, startPos);
643    while (pos != std::string::npos) {
644        if (pos > startPos) {
645            strPart = needTrim ? TrimStr(str.substr(startPos, pos - startPos)) : str.substr(startPos, pos - startPos);
646            out.emplace_back(std::move(strPart));
647        }
648        startPos = pos + sep.size();
649        pos = str.find_first_of(sep, startPos);
650    }
651
652    if (startPos < str.size()) {
653        strPart = needTrim ? TrimStr(str.substr(startPos)) : str.substr(startPos);
654        out.emplace_back(std::move(strPart));
655    }
656}
657
658inline void SplitStr(const std::string& str, const std::string& sep, std::vector<Dimension>& out, bool needTrim = true)
659{
660    out.erase(out.begin(), out.end());
661    if (str.empty() || sep.empty()) {
662        return;
663    }
664    std::string strPart;
665    std::string::size_type startPos = 0;
666    std::string::size_type pos = str.find_first_of(sep, startPos);
667    while (pos != std::string::npos) {
668        if (pos > startPos) {
669            strPart = needTrim ? TrimStr(str.substr(startPos, pos - startPos)) : str.substr(startPos, pos - startPos);
670            if (!strPart.empty()) {
671                out.emplace_back(StringToDimension(std::move(strPart)));
672            }
673        }
674        startPos = pos + sep.size();
675        pos = str.find_first_of(sep, startPos);
676    }
677    if (startPos < str.size()) {
678        strPart = needTrim ? TrimStr(str.substr(startPos)) : str.substr(startPos);
679        if (!strPart.empty()) {
680            out.emplace_back(StringToDimension(std::move(strPart)));
681        }
682    }
683}
684
685const std::string ACE_FORCE_EXPORT FormatString(const char* fmt, ...);
686
687inline bool StartWith(const std::string& dst, const std::string& prefix)
688{
689    return dst.compare(0, prefix.size(), prefix) == 0;
690}
691
692inline bool StartWith(const std::string& str, const char* prefix, size_t prefixLen)
693{
694    return ((str.length() >= prefixLen) && (str.compare(0, prefixLen, prefix) == 0));
695}
696
697inline bool EndWith(const std::string& dst, const std::string& suffix)
698{
699    return (dst.size() >= suffix.size()) && dst.compare(dst.size() - suffix.size(), suffix.size(), suffix) == 0;
700}
701
702inline bool EndWith(const std::string& str, const char* suffix, size_t suffixLen)
703{
704    size_t len = str.length();
705    return ((len >= suffixLen) && (str.compare(len - suffixLen, suffixLen, suffix) == 0));
706}
707
708inline void TransformStrCase(std::string& str, int32_t textCase)
709{
710    if (str.empty()) {
711        return;
712    }
713
714    switch (textCase) {
715        case TEXT_CASE_LOWERCASE:
716            transform(str.begin(), str.end(), str.begin(), ::tolower);
717            break;
718        case TEXT_CASE_UPPERCASE:
719            transform(str.begin(), str.end(), str.begin(), ::toupper);
720            break;
721        default:
722            break;
723    }
724}
725
726ACE_FORCE_EXPORT bool IsAscii(const std::string& str);
727} // namespace OHOS::Ace::StringUtils
728
729#endif // FOUNDATION_ACE_FRAMEWORKS_BASE_UTILS_STRING_UTILS_H
730