应使用 std::regex 配合 icase 选项匹配 12 小时制时间,模式为 r"^(0?[1-9]|1[0-2]):0-5\s*apap$^",并预处理 bom、全角字符等边界情况。

怎么用 std::regex 快速验证 12 小时制时间格式
直接用正则最省事,但得写对模式——^(0?[1-9]|1[0-2]):[0-5][0-9]\s*[APap][Mm]$ 覆盖常见输入,比如 "1:30 PM"、"12:05 am"、"07:45 AM"。注意开头的 0? 允许单数字小时(如 "9:15"),但不接受 "00:30" 或 "13:00"。
实际用时别忘了编译选项:std::regex_constants::icase 让大小写不敏感,否则 "pm" 会匹配失败:
std::regex re(R"^(0?[1-9]|1[0-2]):[0-5][0-9]\s*[APap][Mm]$^", std::regex_constants::icase);
- 空格用
\s*处理,允许冒号后零个或多个空白(包括制表符) - 不要用
^和$以外的锚点,否则"abc1:23 PMdef"可能误判为合法 - Windows 下若用 MSVC,
std::regex对某些模式支持较弱,建议改用std::regex_search配合完整匹配逻辑,而非依赖std::regex_match
为什么不能只靠 sscanf 或 std::stoi 解析后校验
单独解析小时、分钟、AM/PM 字段再判断范围,看似可控,但容易漏掉格式歧义问题。比如 "12:00 XM" 会被 sscanf 成功读出 12 和 0,却忽略 "XM" 不是有效后缀;又比如 " 8:75 AM " 分钟超出范围,但空格+非法数字可能让 sscanf 提前终止或静默失败。
-
sscanf(str.c_str(), "%d:%d %2s", &h, &m, buf)中%2s无法保证只读两个字符,且不校验后续是否还有多余内容 - 手动
find(':')再切片易出界,尤其当字符串为空或无冒号时,substr抛std::out_of_range - 哪怕解析成功,也还得额外检查:小时是否在 1–12,分钟是否在 0–59,后缀是否仅为
"AM"/"PM"(忽略大小写)
std::chrono 能不能直接 parse 12 小时制
不能直接用 std::chrono::parse 处理 12 小时制带 AM/PM 的字符串——标准库目前(C++20/C++23)只支持 24 小时制的 %H、%M,没有原生 %I(12 小时制小时)或 %p(AM/PM)解析器。即使你写 std::chrono::parse(ss, tp, "%I:%M %p"),GCC/Clang 会报错或静默失败。
- 第三方库如
date.h(Howard Hinnant’s date library)支持%I和%p,但引入外部依赖对简单校验来说太重 - 有人尝试先替换
"AM"/"PM"为 24 小时偏移再 parse,但要处理"12 AM" → 0、"12 PM" → 12等边界,反而增加出错概率 - 真正需要后续计算时间差或转换时,才值得上
date.h;仅做合法性判断,正则更轻、更直观
实际项目中容易忽略的边界情况
用户输入永远比测试用例野:多空格、全角冒号、中文 AM/PM、BOM 头、嵌入控制字符……光靠正则还不够。
- 全角冒号
:(U+FF1A)不会被:匹配,需提前统一替换或扩展正则(不推荐,易混乱) - 带 BOM 的 UTF-8 输入(如
"\xEF\xBB\xBF1:30 AM")会让std::regex_match从第一个字节开始失败,应先 strip BOM - AM/PM 后跟句号(
"1:15 A.M.")或空格+换行("7:20 PM\n")需在正则中显式覆盖,例如把[APap][Mm]改成[APap]\.? ?[Mm]\.? - 极端情况如
"1:00 PM"(窄空格 U+202F)或零宽空格,建议在 regex 前用std::erase_if(str, ::isspace)清洗——但注意这会破坏原始空格语义,慎用
真正难的不是写对一个正则,而是想清楚你的输入来源是否可信、要不要容错、以及出错后是拒绝还是自动归一化。没想清这点,代码再“正确”也扛不住真实数据。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











