2025-01-12 19:51:34 +08:00

827 lines
25 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2023, AgiBot Inc.
// All rights reserved.
#pragma once
#include <algorithm>
#include <charconv>
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <map>
#include <regex>
#include <set>
#include <sstream>
#include <string>
#include <vector>
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<std::string, std::string> 解析后的map
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::map<StringType, StringType> SplitToMap(std::string_view source,
std::string_view vsep = "&",
std::string_view msep = "=",
bool trim = true,
bool clear = true) {
std::map<StringType, StringType> 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<std::string,std::string>结构体
* @param[in] vsep 多个kv之间的分隔符
* @param[in] msep 单个kv内部的分隔符
* @return std::string 拼接后的字符串
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::string JoinMap(const std::map<StringType, StringType>& 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<std::string,std::string>中val数据
*
* @param[in] m std::map<std::string,std::string>结构体
* @param[in] key 要获取数据的key
* @param[in] defval 默认字符串当m中没有对应的key时返回defval
* @return std::string 结果
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline const StringType& GetMapItemWithDef(const std::map<StringType, StringType>& 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<std::string> 分割结果
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::vector<StringType> SplitToVec(std::string_view source,
std::string_view sep,
bool trim = true,
bool clear = true) {
std::vector<StringType> 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 <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::string JoinVec(const std::vector<StringType>& 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<std::string> 分割结果
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::set<StringType> SplitToSet(std::string_view source,
std::string_view sep,
bool trim = true,
bool clear = true) {
std::set<StringType> 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 <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::string JoinSet(const std::set<StringType>& 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<std::string_view>(ver1, ".", true, true);
const auto& version2_detail =
SplitToVec<std::string_view>(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<end_ver。若end_ver空则设为999.9.9.9
* @param[in] ver 待检查版本
* @param[in] start_ver 开始版本
* @param[in] end_ver 结束版本
* @return true 在传入的版本之间
* @return false 不在传入的版本之间
*/
inline bool CheckVersionInside(std::string_view ver,
std::string_view start_ver,
std::string_view end_ver) {
return (CmpVersion(ver, start_ver.empty() ? "0.0.0.0" : 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<size_t> 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<char*>(str.c_str() + vec_pos[ii]), nv.data(),
new_len);
} else if (old_len > new_len) {
char* p = const_cast<char*>(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<char*>(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<KeyType> key的集合
*/
template <typename KeyType, typename ValType>
inline std::set<KeyType> GetMapKeys(const std::map<KeyType, ValType>& m) {
std::set<KeyType> re;
for (const auto& it : m) re.emplace(it.first);
return re;
}
/**
* @brief 画一个表格
*
* @param table 表格数据
* @param with_header 是否要画表头
* @return std::string 结果字符串,可以直接打印
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::string DrawTable(
const std::vector<std::vector<StringType>>& table, bool with_header = true) {
size_t row = table.size();
if (row == 0) return "<empty table>";
std::vector<size_t> 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<char>(tolower(c)); }
/**
* @brief 字符转大写
*
* @param c 字符
* @return char 大写字符
*/
inline char CharToUpper(char c) { return static_cast<char>(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<uint8_t>(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<uint8_t>(data[i])) * kPrime;
}
return hash;
}
template <typename T>
inline std::string SSToString(const T& obj) {
std::stringstream ss;
ss << obj;
return ss.str();
}
struct StringHash {
using hash_type = std::hash<std::string_view>;
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