如何在C++17项目中强制fmt将std::string视为UTF-8编码?
让fmt正确识别UTF-8字符串宽度,实现预期的居中对齐效果
哦这个问题我太熟了!fmt默认是按字节数来计算字符串宽度的,而像"あいう"这类UTF-8编码的日文假名每个占3个字节,系统统计下来总字节数是9,所以居中对齐11个宽度时,两边各补1个'-',就出现了你看到的-あいう-。要让fmt把std::string里的内容当作UTF-8字符(按字符数而非字节数)计算宽度,有两种实用的解决办法:
方法1:用fmt::utf8_view直接包装字符串
这是最直接的方式,只需要把你的UTF-8字符串用fmt::utf8_view包裹,就能让fmt按Unicode字符数来计算宽度。记得要包含对应的头文件哦:
#include <fmt/core.h> #include <fmt/unicode.h> // 必须包含这个头文件来启用UTF-8视图 int main() { std::string utf8_str = "あいう"; fmt::print("{:-^11}", fmt::utf8_view(utf8_str)); // 输出结果就是:----あいう---- }
原理很简单:fmt::utf8_view会解析传入的UTF-8字节序列,将每个完整的Unicode字符视为一个宽度单位。这样"あいう"就被统计为3个字符,总宽度11的话,两边各需要补(11-3)/2 = 4个'-',完美符合你的预期。
方法2:自定义std::string的格式化器(全局生效)
如果你的项目里有大量UTF-8编码的std::string需要处理,不想每次都手动写utf8_view,可以通过特化fmt的格式化器,让所有std::string默认按UTF-8规则处理:
#include <fmt/core.h> #include <fmt/unicode.h> // 特化std::string的格式化器,复用utf8_view的格式化逻辑 template <> struct fmt::formatter<std::string> : fmt::formatter<fmt::utf8_view> { template <typename FormatContext> auto format(const std::string& s, FormatContext& ctx) const { return fmt::formatter<fmt::utf8_view>::format(fmt::utf8_view(s), ctx); } }; int main() { std::string utf8_str = "あいう"; fmt::print("{:-^11}", utf8_str); // 直接传入std::string即可,输出----あいう---- }
这个方法一劳永逸,之后所有使用fmt格式化std::string的地方,都会自动按UTF-8字符数计算宽度,非常适合全局统一处理的场景。
额外注意事项
- 务必保证你的std::string确实是合法的UTF-8编码,如果存在无效的字节序列,
utf8_view可能会触发未定义行为; - 如果是在Windows平台运行,记得把控制台的代码页设置为65001(UTF-8),否则可能会出现乱码问题,可以通过命令
chcp 65001或者代码里调用SetConsoleOutputCP(CP_UTF8)来设置。
内容的提问来源于stack exchange,提问作者Frawstcrabs
相关产品推荐
相关产品推荐

