如何在C++编译阶段替换char*字符串中的指定子串?
C++20 constexpr/consteval 编译期字符串替换实现
实现思路
要在编译期完成字符串字面量的子串替换,核心是在编译阶段完成三个步骤:
- 计算原字符串、待替换子串、新子串的长度,统计原串中匹配待替换子串的总次数,推导最终生成字符串的固定长度
- 逐位遍历原字符串,普通字符直接拷贝,匹配到待替换子串时跳过原串对应长度,写入新子串内容
- 返回固定长度的字符数组包装类型,避免返回
const char*带来的悬垂指针问题,同时保证编译期可构造
完整实现代码
#include <array> #include <cstddef> // 编译期计算C风格字符串长度 constexpr size_t str_len(const char* str) { size_t len = 0; while (str[len] != '\0') { len++; } return len; } // 校验字符串指定位置是否匹配目标子串 constexpr bool match_substr(const char* str, size_t pos, const char* sub, size_t sub_len) { for (size_t i = 0; i < sub_len; i++) { if (str[pos + i] != sub[i]) { return false; } } return true; } // 统计原串中待替换子串的匹配总数 constexpr size_t count_match(const char* str, size_t str_len, const char* old_sub, size_t old_len) { size_t cnt = 0; size_t i = 0; while (i <= str_len - old_len) { if (match_substr(str, i, old_sub, old_len)) { cnt++; i += old_len; } else { i++; } } return cnt; } // 核心编译期替换函数,consteval强制逻辑在编译阶段执行 template<size_t OldLen, size_t NewLen> consteval auto replace_sub_str(const char* str, const char (&old_sub)[OldLen], const char (&new_sub)[NewLen]) { // 字符串字面量数组自带末尾'\0',实际子串长度减1 constexpr size_t old_real_len = OldLen - 1; constexpr size_t new_real_len = NewLen - 1; const size_t src_len = str_len(str); const size_t match_cnt = count_match(str, src_len, old_sub, old_real_len); // 计算结果总长度:原长 - 被替换的旧子串总长 + 新子串总长 + 末尾'\0' const size_t res_len = src_len - match_cnt * old_real_len + match_cnt * new_real_len + 1; std::array<char, res_len> res{}; size_t write_pos = 0; size_t read_pos = 0; while (read_pos < src_len) { if (read_pos <= src_len - old_real_len && match_substr(str, read_pos, old_sub, old_real_len)) { // 匹配到旧子串,写入新子串 for (size_t i = 0; i < new_real_len; i++) { res[write_pos++] = new_sub[i]; } read_pos += old_real_len; } else { // 未匹配,直接拷贝原字符 res[write_pos++] = str[read_pos++]; } } res[write_pos] = '\0'; return res; } // 日志宏,固定将子串"XX"替换为"KK",匹配需求示例 #define LOG(x) replace_sub_str(x, "XX", "KK")
使用说明
- 该实现依赖C++20及以上标准,
consteval修饰保证替换逻辑100%在编译阶段执行,无任何运行时开销 - 调用
LOG("hello XX, XX")时,编译期会直接生成内容为"hello KK, KK"的字符数组,可通过.data()方法拿到C风格const char*指针,适配常规日志接口 - 可通过如下编译期断言校验结果正确性,断言不通过则编译失败:
constexpr auto test_result = LOG("hello XX, XX"); static_assert(test_result[6] == 'K' && test_result[7] == 'K'); static_assert(test_result[10] == 'K' && test_result[11] == 'K');
- 如果需要替换其他子串,直接修改
LOG宏中传入的第二、第三个字符串字面量参数即可,无需修改核心替换逻辑
内容的提问来源于stack exchange,提问作者shoujiang ma
相关产品推荐
相关产品推荐

