c++oding="utf-8" ?>
语义版本号合法格式为major.minor.patch(可选预发布标签和构建元数据),需满足无前导零、字段数正确、预发布段不以连字符开头等规则;推荐手写解析器而非正则,因其更可靠、可控且易调试。

什么是语义版本号的合法格式
语义版本号(SemVer)必须严格满足 vMAJOR.MINOR.PATCH 或 MAJOR.MINOR.PATCH 形式,可选带预发布标签(如 -alpha.1)和构建元数据(如 +20240401)。但注意:标准规定 v 前缀不是必需的,但很多工具(如 npm、cargo)接受它;C++ 解析时需明确是否允许该前缀。
合法示例:1.2.3、v2.0.0-rc.1、0.1.0+build.123;非法示例:1.2(缺 PATCH)、1.2.3.4(字段超限)、1.02.3(含前导零)、1.2.-beta(预发布字段不能以连字符开头)。
用正则表达式快速验证(C++11 及以上)
std::regex 能覆盖绝大多数场景,但要注意 PCRE 与 ECMAScript 引擎对 Unicode 和边界处理的差异。推荐使用 ECMAScript 模式,并手动剥离可选 v 前缀再匹配,避免正则过于复杂。
- 核心正则:
R"(^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$)" - 先用
std::string::substr()判断是否以"v"开头,是则取.substr(1)后再匹配 - 必须启用
std::regex_constants::ECMAScript和std::regex_constants::optimize - 注意:GCC libstdc++ 在较老版本(如 GCC 7)中
std::regex实现有 bug,可能 crash 或漏匹配;Clang + libc++ 更可靠
手写解析器更可控(推荐用于生产环境)
正则在边界 case 上易出错(比如 01.2.3 中的前导零),且无法提供结构化结果。手写解析虽代码略长,但逻辑清晰、可调试、无依赖风险。
- 按
'.'分割字符串,检查段数:主版本必须恰好 3 段(MAJOR、MINOR、PATCH),预发布/构建部分单独切分 - 每段数字必须满足:非空、无前导零(除非单个
"0")、纯 ASCII 数字;"0"合法,"00"非法 - 预发布字段(
-后)需进一步按'.'分割,每个子段不能为空,且不能以-开头(但可含-在中间,如beta-1) - 构建字段(
+后)只校验是否为 ASCII 字母/数字/-/.组合,不解释语义 - 示例片段:
std::vector<:string> parts = split(version_str, '.');<br>if (parts.size() if (!is_valid_number(parts[0]) || !is_valid_number(parts[1]) || !is_valid_number(parts[2])) return false;</:string>
第三方库方案(如 semvercpp)的取舍
直接集成 semvercpp 或 libsemver 可省去解析逻辑,但引入外部依赖会增加构建复杂度和 ABI 风险。
-
semvercpp是 header-only,支持 C++17,提供semver::version::parse(),失败时抛std::invalid_argument - 注意其默认行为:接受
v前缀,但不校验预发布字段中的字母顺序(如-alpha ),仅做语法验证 - 若项目已用 Conan 或 vcpkg,可用
vcpkg install semvercpp;否则建议优先手写——毕竟 SemVer 校验逻辑固定,不到 100 行就能覆盖全部 RFC 2119 要求 - 不要用
boost.spirit做此任务:过度设计,编译慢,错误信息难调试
真正容易被忽略的是前导零和预发布字段的连字符位置——这两个点几乎每次手工实现都会漏判一次,建议写单元测试时专门覆盖 "0.0.0"、"1.02.3"、"1.2.3-alpha.1"、"1.2.3-.1" 这四类 case。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











