* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
* @file string.cpp
* @brief 实现基本字符串处理函数
*/
#include <mc/dict.h>
#include <mc/exception.h>
#include <mc/string.h>
#include <mc/variant.h>
#include <cstring>
#include <functional>
#include <type_traits>
#include <stdarg.h>
namespace mc::string {
namespace detail {
void throw_bad_cast_error(const char* type) {
MC_THROW(mc::invalid_arg_exception, "can not cast string to type: ${type}", ("type", type));
}
void throw_overflow_error(const char* type, std::string_view s) {
MC_THROW(mc::overflow_exception, "can not cast string to type ${type}, value ${value} overflow",
("type", type)("value", s));
}
std::pair<int, std::string_view> detect_number_radix(std::string_view s) {
if (s.size() > 1 && s[0] == '0') {
const char c = s[1];
if (c == 'x' || c == 'X') {
return {16, s.substr(2)};
} else if (c == 'b' || c == 'B') {
return {2, s.substr(2)};
} else if (c >= '0' && c <= '7') {
return {8, s.substr(1)};
}
}
return {10, s};
}
std::string_view prepare_number_string(
std::string_view s, int radix, char* buffer, std::size_t buffer_size) noexcept {
if (s.empty()) {
return {};
}
size_t max_len;
switch (radix) {
case 2:
max_len = 64;
break;
case 8:
max_len = 22;
break;
case 16:
max_len = 16;
break;
default:
max_len = 20;
break;
}
if (s.size() > max_len || (s.size() + 1) > buffer_size) {
return {};
}
std::memcpy(buffer, s.data(), s.size());
buffer[s.size()] = '\0';
return std::string_view(buffer, s.size());
}
}
* @brief 尝试将字符串转换为布尔值
* @param s 要转换的字符串
* @param result 转换结果的引用
* @return 是否转换成功
*/
bool try_to_bool(std::string_view s, bool& result) {
if (s.empty()) {
result = false;
return true;
}
if (iequals(s, std::string_view("true", 4)) || s == std::string_view("1", 1)) {
result = true;
return true;
} else if (iequals(s, std::string_view("false", 5)) || s == std::string_view("0", 1)) {
result = false;
return true;
}
return false;
}
bool to_bool_with_default(std::string_view s, bool default_value) {
if (s.empty()) {
return false;
}
bool result;
if (try_to_bool(s, result)) {
return result;
}
return default_value;
}
bool to_bool(std::string_view s) {
if (s.empty()) {
return false;
}
bool result;
if (try_to_bool(s, result)) {
return result;
}
detail::throw_bad_cast_error("bool");
}
bool iequals(std::string_view a, std::string_view b) {
if (a.size() != b.size()) {
return false;
}
return std::equal(a.begin(), a.end(), b.begin(), [](char a, char b) {
return std::tolower(static_cast<unsigned char>(a)) ==
std::tolower(static_cast<unsigned char>(b));
});
}
bool iequals(const char* a, const char* b) {
if (!a || !b) {
return a == b;
}
return iequals(std::string_view(a), std::string_view(b));
}
std::string to_lower(std::string_view s) {
std::string result(s);
to_lower_inplace(result);
return result;
}
std::string to_upper(std::string_view s) {
std::string result(s);
to_upper_inplace(result);
return result;
}
void to_lower_inplace(std::string& s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) {
return std::tolower(c);
});
}
void to_upper_inplace(std::string& s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) {
return std::toupper(c);
});
}
std::string trim(std::string_view s) {
auto start = s.begin();
while (start != s.end() && std::isspace(static_cast<unsigned char>(*start))) {
++start;
}
auto end = s.end();
if (start != s.end()) {
end = s.end();
do {
--end;
} while (std::isspace(static_cast<unsigned char>(*end)) && end != start);
++end;
}
return std::string(start, end);
}
void trim_inplace(std::string& s) {
ltrim_inplace(s);
rtrim_inplace(s);
}
std::string ltrim(std::string_view s) {
auto start = s.begin();
while (start != s.end() && std::isspace(static_cast<unsigned char>(*start))) {
++start;
}
return std::string(start, s.end());
}
void ltrim_inplace(std::string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
}
std::string rtrim(std::string_view s) {
auto end = s.end();
if (s.begin() != s.end()) {
end = s.end();
do {
--end;
} while (end != s.begin() && std::isspace(static_cast<unsigned char>(*end)));
if (!std::isspace(static_cast<unsigned char>(*end))) {
++end;
}
}
return std::string(s.begin(), end);
}
void rtrim_inplace(std::string& s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
[](unsigned char ch) {
return !std::isspace(ch);
})
.base(),
s.end());
}
std::vector<std::string> split(std::string_view s, char delim) {
std::vector<std::string> result;
std::string_view::size_type start = 0;
std::string_view::size_type end = s.find(delim);
while (end != std::string_view::npos) {
result.emplace_back(s.substr(start, end - start));
start = end + 1;
end = s.find(delim, start);
}
if (start < s.size()) {
result.emplace_back(s.substr(start));
}
return result;
}
std::vector<std::string> split(std::string_view s, std::string_view delim) {
std::vector<std::string> result;
if (delim.empty()) {
result.emplace_back(s);
return result;
}
std::string_view::size_type start = 0;
std::string_view::size_type end = s.find(delim);
while (end != std::string_view::npos) {
result.emplace_back(s.substr(start, end - start));
start = end + delim.size();
end = s.find(delim, start);
}
if (start < s.size()) {
result.emplace_back(s.substr(start));
}
return result;
}
std::string join(const std::vector<std::string>& v, std::string_view delim) {
if (v.empty()) {
return {};
}
std::string result;
size_t total_size = 0;
for (const auto& s : v) {
total_size += s.size();
}
total_size += delim.size() * (v.size() - 1);
result.reserve(total_size);
for (size_t i = 0; i < v.size(); ++i) {
result += v[i];
if (i < v.size() - 1) {
result.append(delim.data(), delim.size());
}
}
return result;
}
bool starts_with(std::string_view s, std::string_view prefix) {
if (prefix.empty()) {
return true;
}
return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix;
}
bool ends_with(std::string_view s, std::string_view suffix) {
return s.size() >= suffix.size() && s.substr(s.size() - suffix.size()) == suffix;
}
std::string_view longest_common_prefix(std::string_view s1, std::string_view s2) {
size_t i = 0;
while (i < s1.size() && i < s2.size() && s1[i] == s2[i]) {
++i;
}
return s1.substr(0, i);
}
std::string replace_all(std::string_view s, std::string_view from, std::string_view to) {
if (from.empty()) {
return std::string(s);
}
std::string result;
result.reserve(s.size());
size_t last_pos = 0;
size_t pos = 0;
while ((pos = s.find(from, last_pos)) != std::string_view::npos) {
result.append(s.data() + last_pos, pos - last_pos);
result.append(to.data(), to.size());
last_pos = pos + from.size();
}
result.append(s.data() + last_pos, s.size() - last_pos);
return result;
}
void replace_all_inplace(std::string& s, std::string_view from, std::string_view to) {
if (from.empty()) {
return;
}
size_t start_pos = 0;
while ((start_pos = s.find(from, start_pos)) != std::string::npos) {
s.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}
bool contains(std::string_view s, std::string_view substring) {
return s.find(substring) != std::string_view::npos;
}
bool icontains(std::string_view s, std::string_view substring) {
if (s.empty() || substring.empty() || s.size() < substring.size()) {
return substring.empty();
}
if (s.size() <= 1024 && substring.size() <= 1024) {
std::string s_lower = to_lower(s);
std::string substring_lower = to_lower(substring);
return contains(s_lower, substring_lower);
}
auto is_equal_ignore_case = [](char a, char b) {
return std::tolower(static_cast<unsigned char>(a)) ==
std::tolower(static_cast<unsigned char>(b));
};
for (size_t i = 0; i <= s.size() - substring.size(); ++i) {
bool found = true;
for (size_t j = 0; j < substring.size(); ++j) {
if (!is_equal_ignore_case(s[i + j], substring[j])) {
found = false;
break;
}
}
if (found) {
return true;
}
}
return false;
}
* @brief 获取子字符串,支持负数索引
*/
std::string_view substr(std::string_view s, int start, int end) {
const std::size_t length = s.length();
if (length == 0) {
return "";
}
int adjusted_start = start;
if (adjusted_start < 0) {
adjusted_start = static_cast<int>(length) + adjusted_start;
}
int adjusted_end = end;
if (adjusted_end < 0) {
adjusted_end = static_cast<int>(length) + adjusted_end;
}
if (adjusted_start < 0) {
adjusted_start = 0;
}
if (adjusted_start >= static_cast<int>(length)) {
return "";
}
if (adjusted_end >= static_cast<int>(length)) {
adjusted_end = static_cast<int>(length) - 1;
}
if (adjusted_end < adjusted_start) {
return "";
}
std::size_t substr_length = adjusted_end - adjusted_start + 1;
return s.substr(adjusted_start, substr_length);
}
* @brief 获取子字符串,第二个参数指定长度而非结束位置
*/
std::string_view substring(std::string_view s, int start, std::size_t length) {
const std::size_t str_length = s.length();
if (str_length == 0) {
return "";
}
int adjusted_start = start;
if (adjusted_start < 0) {
adjusted_start = static_cast<int>(str_length) + adjusted_start;
}
if (adjusted_start < 0) {
return "";
}
if (adjusted_start >= static_cast<int>(str_length)) {
return "";
}
if (length == std::string::npos ||
adjusted_start + static_cast<std::size_t>(length) > str_length) {
length = str_length - adjusted_start;
}
return s.substr(adjusted_start, length);
}
* @brief 使用固定宽度格式化字符串,不足用空格填充,并追加到目标字符串
*/
void fixed_width_append(std::string& result, size_t width, std::string_view s, bool left_align) {
if (s.length() >= width) {
result.append(s.data(), width);
return;
}
size_t padding = width - s.length();
if (left_align) {
result.append(s.data(), s.length());
result.append(padding, ' ');
} else {
result.append(padding, ' ');
result.append(s.data(), s.length());
}
}
bool is_valid_utf8(std::string_view s) {
const unsigned char* bytes = reinterpret_cast<const unsigned char*>(s.data());
size_t length = s.size();
for (size_t i = 0; i < length;) {
if (bytes[i] <= 0x7F) {
i += 1;
continue;
}
else if ((bytes[i] & 0xE0) == 0xC0) {
if (i + 1 >= length) {
return false;
}
if ((bytes[i + 1] & 0xC0) != 0x80) {
return false;
}
unsigned int code_point = ((bytes[i] & 0x1F) << 6) | (bytes[i + 1] & 0x3F);
if (code_point < 0x80) {
return false;
}
i += 2;
}
else if ((bytes[i] & 0xF0) == 0xE0) {
if (i + 2 >= length) {
return false;
}
if ((bytes[i + 1] & 0xC0) != 0x80 || (bytes[i + 2] & 0xC0) != 0x80) {
return false;
}
unsigned int code_point =
((bytes[i] & 0x0F) << 12) | ((bytes[i + 1] & 0x3F) << 6) | (bytes[i + 2] & 0x3F);
if (code_point < 0x800 || (code_point >= 0xD800 && code_point <= 0xDFFF)) {
return false;
}
i += 3;
}
else if ((bytes[i] & 0xF8) == 0xF0) {
if (i + 3 >= length) {
return false;
}
if ((bytes[i + 1] & 0xC0) != 0x80 || (bytes[i + 2] & 0xC0) != 0x80 ||
(bytes[i + 3] & 0xC0) != 0x80) {
return false;
}
unsigned int code_point = ((bytes[i] & 0x07) << 18) | ((bytes[i + 1] & 0x3F) << 12) |
((bytes[i + 2] & 0x3F) << 6) | (bytes[i + 3] & 0x3F);
if (code_point < 0x10000 || code_point > 0x10FFFF) {
return false;
}
i += 4;
}
else {
return false;
}
}
return true;
}
void to_string(std::string& result, double value) {
char buffer[64];
double intpart;
if (modf(value, &intpart) == 0.0) {
snprintf(buffer, sizeof(buffer), "%.0f", value);
} else {
snprintf(buffer, sizeof(buffer), "%.6f", value);
char* end = buffer + strlen(buffer) - 1;
while (end > buffer && *end == '0') {
*end-- = '\0';
}
if (end > buffer && *end == '.') {
*end = '\0';
}
}
result.append(buffer);
}
std::string to_string(double value) {
std::string result;
to_string(result, value);
return result;
}
std::string to_string(bool value) {
std::string result;
to_string(result, value);
return result;
}
void to_string(std::string& result, bool value) {
result.append(value ? "true" : "false");
}
}