// Copyright (c) 2023, AgiBot Inc. // All rights reserved. #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace aimrt::common::util { /** * @brief 修剪字符串 * * @param[inout] s 待处理字符串 * @return std::string& 处理后的字符串s的引用 */ inline std::string& Trim(std::string& s) { if (s.empty()) return s; s.erase(s.find_last_not_of(' ') + 1); s.erase(0, s.find_first_not_of(' ')); return s; } inline std::string_view& Trim(std::string_view& s) { if (s.empty()) return s; auto p = s.find_last_not_of(' '); s = s.substr(0, (p != std::string_view::npos) ? (p + 1) : 0); p = s.find_first_not_of(' '); s = s.substr((p != std::string_view::npos) ? p : s.size()); return s; } /** * @brief 将类似于a=1&b=2&c=3这样的字符串解析到map中 * @note * 分割原则:在两个vsep之间的子字符串,如果包含msep,则以msep为界分为key、val * @param[in] source 待分割字符串 * @param[in] vsep 多个kv之间的分隔符,不可为空 * @param[in] msep 单个kv内部的分隔符,不可为空 * @param[in] trim 是否去除k和v里的空格 * @param[in] clear 是否去除key为空的情况 * @return std::map 解析后的map */ template requires(std::is_same_v || std::is_same_v) inline std::map SplitToMap(std::string_view source, std::string_view vsep = "&", std::string_view msep = "=", bool trim = true, bool clear = true) { std::map result; if (source.empty() || vsep.empty() || msep.empty() || vsep == msep) return result; size_t v_pos_end = 0, v_pos_start = 0; do { v_pos_end = source.find(vsep, v_pos_start); if (v_pos_end == std::string_view::npos) v_pos_end = source.length(); if (v_pos_end > v_pos_start) { auto kv_str = source.substr(v_pos_start, v_pos_end - v_pos_start); size_t msep_pos = kv_str.find(msep); if (msep_pos != std::string_view::npos) { auto first = kv_str.substr(0, msep_pos); auto second = kv_str.substr(msep_pos + msep.size()); if (trim) { Trim(first); if (!(clear && first.empty())) { result[StringType(first)] = StringType(Trim(second)); } } else { if (!(clear && first.empty())) { result[StringType(first)] = StringType(second); } } } } v_pos_start = v_pos_end + vsep.size(); } while (v_pos_end < source.length()); return result; } /** * @brief 拼接map为a=1&b=2&c=3形式的string * * @param[in] kvmap std::map结构体 * @param[in] vsep 多个kv之间的分隔符 * @param[in] msep 单个kv内部的分隔符 * @return std::string 拼接后的字符串 */ template requires(std::is_same_v || std::is_same_v) inline std::string JoinMap(const std::map& kvmap, std::string_view vsep = "&", std::string_view msep = "=") { std::stringstream result; for (auto itr = kvmap.begin(); itr != kvmap.end(); ++itr) { if (itr != kvmap.begin()) result << vsep; result << itr->first << msep << itr->second; } return result.str(); } /** * @brief 带默认值的获取map中val数据 * * @param[in] m std::map结构体 * @param[in] key 要获取数据的key * @param[in] defval 默认字符串,当m中没有对应的key时,返回defval * @return std::string 结果 */ template requires(std::is_same_v || std::is_same_v) inline const StringType& GetMapItemWithDef(const std::map& m, const StringType& key, const StringType& defval = "") { auto finditr = m.find(key); return (finditr != m.end()) ? (finditr->second) : defval; } /** * @brief 向s中添加kv字段 * * @param[inout] s 待处理字符串 * @param[in] key key * @param[in] val val * @param[in] vsep 多个kv之间的分隔符 * @param[in] msep 单个kv内部的分隔符 * @return std::string& 拼接后的字符串s的引用 */ inline std::string& AddKV(std::string& s, std::string_view key, std::string_view val, std::string_view vsep = "&", std::string_view msep = "=") { if (!s.empty()) s += vsep; s += key; s += msep; s += val; return s; } /** * @brief 从类似于a=1&b=2&c=3这样的字符串中得到key对应的val * @note * 比直接分割成map再find要快一些,但如果有多次调用,还是建议先分割成map再find * @param[in] str 待处理字符串 * @param[in] key key,不可为空,不能包含vsep或msep,否则返回空结果 * @param[in] vsep 多个kv之间的分隔符,不可为空 * @param[in] msep 单个kv内部的分隔符,不可为空 * @param[in] trim 是否不计空格 * @return std::string key对应的val */ inline std::string_view GetValueFromStrKV(std::string_view str, std::string_view key, std::string_view vsep = "&", std::string_view msep = "=", bool trim = true) { if (key.empty() || vsep.empty() || msep.empty() || vsep == msep) return ""; if (key.find(vsep) != std::string_view::npos || key.find(msep) != std::string_view::npos) return ""; size_t key_pos = str.find(key); while (key_pos != std::string_view::npos) { size_t msep_pos = str.find(msep, key_pos + key.size()); if (msep_pos == std::string_view::npos) return ""; size_t key_start_pos = str.rfind(vsep, key_pos); if (key_start_pos == std::string_view::npos) key_start_pos = 0; else key_start_pos += vsep.length(); bool right_key_flag = false; if (trim) { auto real_key = str.substr(key_start_pos, msep_pos - key_start_pos); Trim(real_key); if (real_key == key) right_key_flag = true; } else { if ((key_start_pos == key_pos) && (msep_pos == (key_pos + key.size()))) right_key_flag = true; } if (right_key_flag) { size_t val_start_pos = msep_pos + msep.size(); size_t val_end_pos = str.find(vsep, val_start_pos); if (val_end_pos == std::string_view::npos) val_end_pos = str.length(); if (trim) { auto re = str.substr(val_start_pos, val_end_pos - val_start_pos); return Trim(re); } return str.substr(val_start_pos, val_end_pos - val_start_pos); } key_pos = str.find(key, msep_pos + msep.length()); } return ""; } /** * @brief 分割字符串到vector * * @param[in] source 待处理字符串 * @param[in] sep 分隔符 * @param[in] trim 是否对每项结果去除空格 * @param[in] clear 是否去除空项 * @return std::vector 分割结果 */ template requires(std::is_same_v || std::is_same_v) inline std::vector SplitToVec(std::string_view source, std::string_view sep, bool trim = true, bool clear = true) { std::vector result; if (source.empty() || sep.empty()) return result; size_t pos_end = 0, pos_start = 0; do { pos_end = source.find(sep, pos_start); if (pos_end == std::string_view::npos) pos_end = source.length(); auto sub_str = source.substr(pos_start, pos_end - pos_start); if (trim) Trim(sub_str); if (!(clear && sub_str.empty())) result.emplace_back(sub_str); pos_start = pos_end + sep.size(); } while (pos_end < source.length()); return result; } /** * @brief 拼接vector到string * * @param[in] vec 待处理vector * @param[in] sep 分隔符 * @return std::string 拼接后的字符串 */ template requires(std::is_same_v || std::is_same_v) inline std::string JoinVec(const std::vector& vec, std::string_view sep) { std::string result; for (auto itr = vec.begin(); itr != vec.end(); ++itr) { if (itr != vec.begin()) result += sep; result += *itr; } return result; } /** * @brief 分割字符串到set,自动去重 * * @param[in] source 待处理字符串 * @param[in] sep 分隔符 * @param[in] trim 是否对每项结果去除空格 * @param[in] clear 是否去除空项 * @return std::set 分割结果 */ template requires(std::is_same_v || std::is_same_v) inline std::set SplitToSet(std::string_view source, std::string_view sep, bool trim = true, bool clear = true) { std::set result; if (source.empty() || sep.empty()) return result; size_t pos_end = 0, pos_start = 0; do { pos_end = source.find(sep, pos_start); if (pos_end == std::string_view::npos) pos_end = source.length(); auto sub_str = source.substr(pos_start, pos_end - pos_start); if (trim) Trim(sub_str); if (!(clear && sub_str.empty())) result.emplace(sub_str); pos_start = pos_end + sep.size(); } while (pos_end < source.length()); return result; } /** * @brief 拼接set到string * * @param[in] st 待处理set * @param[in] sep 分隔符 * @return std::string 拼接后的字符串 */ template requires(std::is_same_v || std::is_same_v) inline std::string JoinSet(const std::set& st, std::string_view sep) { std::string result; for (auto itr = st.begin(); itr != st.end(); ++itr) { if (itr != st.begin()) result += sep; result += *itr; } return result; } /** * @brief 判断key字符串是否在list字符串中,如"123"是否在"123,456,789"中 * @note * 比直接分割成vector再find要快一些,但如果有多次调用,还是建议先分割成vector再find * @param[in] str list字符串 * @param[in] key key字符串,不可为空,不能包含sep * @param[in] sep list的分隔符,不可为空 * @param[in] trim 是否不计空格 * @return true key在list中 * @return false key不在list中 */ inline bool CheckIfInList(std::string_view str, std::string_view key, std::string_view sep = ",", bool trim = true) { if (key.empty() || sep.empty()) return false; if (key.find(sep) != std::string_view::npos) return false; size_t key_pos = str.find(key); while (key_pos != std::string_view::npos) { size_t key_start_pos = str.rfind(sep, key_pos); if (key_start_pos == std::string_view::npos) key_start_pos = 0; else key_start_pos += sep.length(); size_t key_end_pos = str.find(sep, key_pos + key.length()); if (key_end_pos == std::string_view::npos) key_end_pos = str.length(); if (trim) { auto real_key = str.substr(key_start_pos, key_end_pos - key_start_pos); Trim(real_key); if (real_key == key) return true; } else { if ((key_start_pos == key_pos) && (key_end_pos == (key_pos + key.size()))) return true; } key_pos = str.find(key, key_end_pos + sep.length()); } return false; } /** * @brief 比较版本,如6.1.1 6.2.8 * * @param[in] ver1 版本1 * @param[in] ver2 版本2 * @return int 返回1是大于,返回0是相等,返回-1是小于 */ inline int CmpVersion(std::string_view ver1, std::string_view ver2) { const auto& version1_detail = SplitToVec(ver1, ".", true, true); const auto& version2_detail = SplitToVec(ver2, ".", true, true); size_t idx = 0; for (idx = 0; idx < version1_detail.size() && idx < version2_detail.size(); ++idx) { int ver1_num = 0, ver2_num = 0; auto [ptr1, ec1] = std::from_chars(version1_detail[idx].data(), version1_detail[idx].data() + version1_detail[idx].size(), ver1_num); auto [ptr2, ec2] = std::from_chars(version2_detail[idx].data(), version2_detail[idx].data() + version2_detail[idx].size(), ver2_num); if (ec1 != std::errc() || ec2 != std::errc() || ptr1 != version1_detail[idx].data() + version1_detail[idx].size() || ptr2 != version2_detail[idx].data() + version2_detail[idx].size()) { throw std::runtime_error("Invalid version format"); } if (ver1_num < ver2_num) return -1; if (ver1_num > ver2_num) return 1; } if (idx == version1_detail.size() && idx == version2_detail.size()) { return 0; } return version1_detail.size() > version2_detail.size() ? 1 : -1; } /** * @brief 检查版本是否处于设定版本之间 * @note 需要保证start_ver= 0 && CmpVersion(ver, end_ver.empty() ? "999.9.9.9" : end_ver) <= 0); } /** * @brief 将一个字符串中指定字符串ov换为字符串nv * * @param[inout] str 待替换字符串 * @param[in] ov 要被替换的子字符串 * @param[in] nv 要替换成的子字符串 * @return std::string& 替换后的字符串str的引用 */ inline std::string& ReplaceString(std::string& str, std::string_view ov, std::string_view nv) { if (str.empty() || ov.empty()) return str; std::vector vec_pos; size_t pos = 0, old_len = ov.size(), new_len = nv.size(); while ((pos = str.find(ov, pos)) != std::string::npos) { vec_pos.emplace_back(pos); pos += old_len; } size_t& vec_len = pos = vec_pos.size(); if (vec_len) { if (old_len == new_len) { for (size_t ii = 0; ii < vec_len; ++ii) memcpy(const_cast(str.c_str() + vec_pos[ii]), nv.data(), new_len); } else if (old_len > new_len) { char* p = const_cast(str.c_str()) + vec_pos[0]; vec_pos.emplace_back(str.size()); for (size_t ii = 0; ii < vec_len; ++ii) { memcpy(p, nv.data(), new_len); p += new_len; size_t cplen = vec_pos[ii + 1] - vec_pos[ii] - old_len; memmove(p, str.c_str() + vec_pos[ii] + old_len, cplen); p += cplen; } str.resize(p - str.c_str()); } else { size_t diff = new_len - old_len; vec_pos.emplace_back(str.size()); str.resize(str.size() + diff * vec_len); char* p = const_cast(str.c_str()) + str.size(); for (size_t ii = vec_len - 1; ii < vec_len; --ii) { size_t cplen = vec_pos[ii + 1] - vec_pos[ii] - old_len; p -= cplen; memmove(p, str.c_str() + vec_pos[ii] + old_len, cplen); p -= new_len; memcpy(p, nv.data(), new_len); } } } return str; } /** * @brief 判断字符串是否为数字和字母组成 * * @param[in] str 待判断字符串 * @return true 全部为数字和字母组成 * @return false 不全为数字和字母组成 */ inline bool IsAlnumStr(std::string_view str) { if (str.length() == 0) return false; for (const auto& c : str) { if (!isalnum(c)) return false; } return true; } /** * @brief 判断字符串是否为数字组成 * * @param[in] str 待判断字符串 * @return true 全部为数字组成 * @return false 不全为数字组成 */ inline bool IsDigitStr(std::string_view str) { if (str.length() == 0) return false; for (const auto& c : str) { if (c > '9' || c < '0') return false; } return true; } /** * @brief 获取map中的key的集合 * * @tparam KeyType map中key的类型 * @tparam ValType map中val的类型 * @param m 输入map * @return std::set key的集合 */ template inline std::set GetMapKeys(const std::map& m) { std::set re; for (const auto& it : m) re.emplace(it.first); return re; } /** * @brief 画一个表格 * * @param table 表格数据 * @param with_header 是否要画表头 * @return std::string 结果字符串,可以直接打印 */ template requires(std::is_same_v || std::is_same_v) inline std::string DrawTable( const std::vector>& table, bool with_header = true) { size_t row = table.size(); if (row == 0) return ""; std::vector column_width; for (auto& row_item : table) { if (column_width.size() < row_item.size()) column_width.resize(row_item.size()); for (size_t ii = 0; ii < row_item.size(); ++ii) column_width[ii] = std::max(column_width[ii], row_item[ii].size()); } for (auto& item : column_width) item += 2; size_t column = column_width.size(); std::stringstream result; // first line for (size_t jj = 0; jj < column; ++jj) { result << "+" << std::setfill('-') << std::setw(column_width[jj]) << "-"; } result << "+\n"; for (size_t ii = 0; ii < row; ++ii) { for (size_t jj = 0; jj < column; ++jj) { std::string cur_data = " "; if (table[ii].size() > jj) cur_data += table[ii][jj]; result << "|" << std::left << std::setfill(' ') << std::setw(column_width[jj]) << cur_data; } result << "|\n"; // table header if (with_header && ii == 0) { for (size_t jj = 0; jj < column; ++jj) { result << "+" << std::setfill('-') << std::setw(column_width[jj]) << "-"; } result << "+\n"; } } // last line for (size_t jj = 0; jj < column; ++jj) { result << "+" << std::setfill('-') << std::setw(column_width[jj]) << "-"; } result << "+\n"; return result.str(); } /** * @brief 将字符串中形如 ${XXX_ENV} 的部分替换为对应环境变量的值 * * @param input * @return std::string */ inline std::string ReplaceEnvVars(std::string_view input) { std::regex pattern(R"(\$\{([^}]+)\})"); std::smatch match; std::string result(input); size_t cur_pos = 0; while (std::regex_search(result.cbegin() + cur_pos, result.cend(), match, pattern)) { std::string env_name = match[1].str(); const char* env_val = std::getenv(env_name.c_str()); if (env_val == nullptr) env_val = ""; result.replace(cur_pos + match.position(0), match.length(0), env_val); cur_pos += (match.position(0) + strlen(env_val)); } return result; } /** * @brief 字符转小写 * * @param c 字符 * @return char 小写字符 */ inline char CharToLower(char c) { return static_cast(tolower(c)); } /** * @brief 字符转大写 * * @param c 字符 * @return char 大写字符 */ inline char CharToUpper(char c) { return static_cast(toupper(c)); } /** * @brief 字符串转小写 * * @param str 字符串 * @return std::string& 小写字符串 */ inline std::string& StrToLower(std::string& str) { std::transform(str.begin(), str.end(), str.begin(), CharToLower); return str; } /** * @brief 字符串转小写 * * @param str 字符串 * @return std::string 小写字符串 */ inline std::string StrToLower(std::string_view str) { std::string result; result.resize(str.size()); std::transform(str.begin(), str.end(), result.begin(), CharToLower); return result; } /** * @brief 字符串转大写 * * @param str 字符串 * @return std::string& 大写字符串 */ inline std::string& StrToUpper(std::string& str) { std::transform(str.begin(), str.end(), str.begin(), CharToUpper); return str; } /** * @brief 字符串转大写 * * @param str 字符串 * @return std::string 大写字符串 */ inline std::string StrToUpper(std::string_view str) { std::string result; result.resize(str.size()); std::transform(str.begin(), str.end(), result.begin(), CharToUpper); return result; } /** * @brief 比较两个字符串忽略大小写后是否相等 * * @param str1 * @param str2 * @return true * @return false */ inline bool CheckIEqual(std::string_view str1, std::string_view str2) { if (str1.size() != str2.size()) return false; return std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) { return std::tolower(a) == std::tolower(b); }); } /** * @brief 字符串转为标题形式,单词首字符大写 * * @param str 字符串 * @return std::string& 标题形式字符串 */ inline std::string& StrToTitleCase(std::string& str) { std::string::iterator it = str.begin(); *it = CharToUpper(*it); for (; it != str.end() - 1; ++it) { if (*it == ' ') { *(it + 1) = CharToUpper(*(it + 1)); } } return str; } /** * @brief standardise a path string * * @param path * @return std::string& */ inline std::string& StandardisePath(std::string& path) { if (path.empty()) return (path = "/"); std::replace(path.begin(), path.end(), '\\', '/'); if (path[path.length() - 1] != '/') path += '/'; return path; } /** * @brief judge if str start with pattern * * @param str * @param pattern * @param ignore_case if ignore the case * @return true * @return false */ inline bool StartsWith(std::string_view str, std::string_view pattern, bool ignore_case = false) { const size_t str_len = str.length(); const size_t pattern_len = pattern.length(); if (str_len < pattern_len || pattern_len == 0) return false; if (ignore_case) { for (size_t i = 0; i < pattern_len; ++i) { if (tolower(pattern[i]) != tolower(str[i])) return false; } return true; } for (size_t i = 0; i < pattern_len; ++i) { if (pattern[i] != str[i]) return false; } return true; } /** * @brief judge if str end with pattern * * @param str * @param pattern * @param ignore_case if ignore the case * @return true * @return false */ inline bool EndsWith(std::string_view str, std::string_view pattern, bool ignore_case = false) { const size_t str_len = str.length(); const size_t pattern_len = pattern.length(); if (str_len < pattern_len || pattern_len == 0) return false; const size_t& begin_pos = str_len - pattern_len; if (ignore_case) { for (size_t i = 0; i < pattern_len; ++i) { if (tolower(pattern[i]) != tolower(str[begin_pos + i])) return false; } return true; } for (size_t i = 0; i < pattern_len; i++) { if (pattern[i] != str[begin_pos + i]) return false; } return true; } /** * @brief fnv1a hash算法,64位 * * @param data * @param len * @return uint64_t hash值 */ inline uint64_t Hash64Fnv1a(const char* data, size_t len) { constexpr uint64_t kPrime = 0x100000001b3; uint64_t hash = 0xcbf29ce484222325; for (size_t i = 0; i < len; ++i) { hash = (hash ^ static_cast(data[i])) * kPrime; } return hash; } /** * @brief fnv1a hash算法,32位 * * @param data * @param len * @return uint32_t hash值 */ inline uint32_t Hash32Fnv1a(const char* data, size_t len) { constexpr uint32_t kPrime = 0x1000193; uint32_t hash = 0x811c9dc5; for (size_t i = 0; i < len; ++i) { hash = (hash ^ static_cast(data[i])) * kPrime; } return hash; } template inline std::string SSToString(const T& obj) { std::stringstream ss; ss << obj; return ss.str(); } struct StringHash { using hash_type = std::hash; using is_transparent = void; std::size_t operator()(const char* str) const { return hash_type{}(str); } std::size_t operator()(std::string_view str) const { return hash_type{}(str); } std::size_t operator()(std::string const& str) const { return hash_type{}(str); } }; } // namespace aimrt::common::util