应先确认字符串编码,再用std::mbrtoc32解码为char32_t,判断码点是否在U+4E00–U+9FFF、U+3400–U+4DBF等中文区间;避免字节级启发式判断或依赖locale的宽字符函数。

如何用 C++ 判断一个字符是否为中文
中文字符在 UTF-8 编码下是 3 字节序列,首字节范围为 0xE0–0xEF;在 GBK 下是 2 字节,首字节为 0xB0–0xF7。但直接按字节判断极易出错——比如把 UTF-8 中的非中文多字节字符(如 emoji、日文假名)误判为中文,或把 UTF-8 的中间字节单独当成乱码处理。
更可靠的方式是:先确认字符串编码,再用标准库或第三方库做 Unicode 字符级判断。实际项目中,绝大多数现代 C++ 环境默认处理 UTF-8 字符串,且不支持 std::iswctype 直接识别中文(它依赖 locale,而 "zh_CN.UTF-8" 下也不保证覆盖全部汉字)。
- 不要用
static_cast<unsigned char>(c) >= 0xB0</unsigned>这类 GBK 启发式判断——输入是 UTF-8 时必然崩溃或漏删 - 不要对
std::string单个char调用std::iswalpha或std::iswprint——它们作用于wchar_t,且不区分中/日/韩 - 若确定输入为 UTF-8,应先用
std::mbrtoc32或std::codecvt_utf8(已弃用)/C++20std::from_chars(不适用)逐个解码成char32_t,再判断码点范围
用 C++20 std::ranges + std::utf8_decode 做安全过滤(推荐)
C++20 引入了 std::utf8_decode(位于 <bit>?错,实际在 <string_view> 和 <iterator> 配合使用),但更实用的是手动实现轻量 UTF-8 解码循环——因为标准库至今没提供开箱即用的“UTF-8 字符迭代器”。
以下方法兼容 C++17 及以上,不依赖第三方库,只删除非中文 Unicode 字符(U+4E00–U+9FFF 是基本汉字区,U+3400–U+4DBF 是扩展 A,U+20000–U+2A6DF 是扩展 B,生产环境建议至少覆盖前两者):
立即学习“C++免费学习笔记(深入)”;
#include <string>
#include <cuchar>
#include <vector>
<p>std::string keep_only_chinese(const std::string& s) {
std::string result;
result.reserve(s.size()); // 预留空间,避免多次 realloc
const char<em> p = s.c_str();
while (</em>p) {
char32_t cp;
int len = std::mbrtoc32(&cp, p, 4, nullptr);
if (len == static_cast<int>(std::size_t(-1)) || len == static_cast<int>(std::size_t(-2))) {
// 无效或不完整 UTF-8 序列,跳过该字节(或按需报错)
p++;
continue;
}
if (len > 0 && (
(cp >= 0x4E00 && cp <= 0x9FFF) || // 基本汉字
(cp >= 0x3400 && cp <= 0x4DBF) || // 扩展 A
(cp >= 0x20000 && cp <= 0x2A6DF) // 扩展 B(需确保 char32_t 足够宽)
)) {
// 合法中文码点,转回 UTF-8 写入 result
char buf[4];
int wlen = std::c32rtomb(buf, cp, nullptr);
if (wlen > 0) result.append(buf, wlen);
}
p += len;
}
return result;
}</p>-
std::mbrtoc32是 C++11 引入的标准函数,跨平台可用(MSVC、GCC、Clang 均支持) - 注意
std::c32rtomb第三个参数传nullptr表示不检查状态,适合单次转换 - 扩展 B 区(U+20000 起)需要
char32_t至少 21 位,现代编译器均满足,但若目标平台老旧,可先去掉该段判断
为什么不用 ICU 或 Boost.Locale
ICU 功能完备,能精准识别 UCHAR_SCRIPT_HAN(含中日韩统一汉字),但引入 ICU 意味着:静态链接增加数 MB 体积,交叉编译需额外配置,Windows 下 DLL 依赖易出错;Boost.Locale 同样重量级,且 boost::locale::is<code> 对中文的支持依赖底层 ICU 或 Windows API,接口复杂度远超需求。
如果你的场景只是「从日志/用户输入里粗筛中文」,上述手写 UTF-8 解码已足够;只有当需要区分「简体中文」「繁体中文」「日文汉字」「古籍异体字」时,才值得上 ICU。
- 别为了删非中文字符,把构建系统拖进 ICU 的编译泥潭
- 若已有 ICU 在项目中,直接用
icu::UnicodeString+uscript_getScript更稳 - Python 用户常写的
re.sub(r'[^\u4e00-\u9fff]', '', s)在 C++ 里没有等价正则原语——std::regex对 Unicode 码点支持极差,别试
常见错误现象与调试建议
传入 GBK 编码字符串却用 UTF-8 解码逻辑,会导致 std::mbrtoc32 返回 -1(非法序列),整个字符串被清空;反过来,UTF-8 输入用 GBK 逻辑扫描,会把中文拆成多个无效字节,结果乱码或崩溃。
- 运行前先用
xxd或file -i确认字符串真实编码,而不是看编辑器右下角显示 - 测试用例必须包含边界字符:如「?」(U+20BB7,扩展 A)、「〇」(U+3007,中文数字零,不在 4E00–9FFF 内,需额外加判断)
- 若发现结果为空,用
std::cout << std::hex << (int)(unsigned char)s[0]打印前几个字节,确认是否真为 UTF-8
最易被忽略的是:你根本不知道输入源的编码。没有编码声明的字符串就像没有说明书的硬件——所有后续操作都建立在假设之上。


















