第六章 数学与字符串:math命令、string命令详解
说实话,CMake 里的数学和字符串操作,看着简单,用起来坑不少。我最早接触 CMake 时,觉得这玩意儿不就是个构建工具吗?怎么还要处理字符串?后来项目做多了才发现——构建系统里到处是字符串拼接、版本号比较、路径处理。今天咱们就把这块彻底讲透。
6.1 math 命令:CMake 里的计算器
先聊聊数学运算。CMake 的 math 命令,说白了就是个整数计算器。它支持加减乘除、取模、位运算。语法很简单:
math(EXPR <output_variable> <expression>)
看个例子:
set(A 10)
set(B 3)
math(EXPR RESULT "${A} + ${B} * 2")
message("结果: ${RESULT}") # 输出 16
注意,CMake 的 math 遵循 C 语言的运算符优先级。乘除优先于加减,括号可以改变优先级。我在项目中遇到过一个问题:有人写 math(EXPR X "10 / 3"),期望得到 3.333,结果却是 3。为什么?因为 CMake 只支持整数运算,小数部分直接截断。
位运算也支持,不过用得少:
math(EXPR FLAGS "0xFF & 0x0F") # 按位与,结果 15
我个人习惯把 math 用在计数器、循环索引、或者简单的配置计算上。复杂的数学逻辑,建议交给 Python 脚本或者外部工具。
6.2 string 命令:字符串处理全家桶
字符串操作才是 CMake 的重头戏。string 命令功能非常丰富,我把它分成几类来讲。
6.2.1 字符串查找与替换
查找子串用 FIND:
set(TEXT "Hello, CMake World!")
string(FIND "${TEXT}" "CMake" POS)
message("CMake 的位置: ${POS}") # 输出 7
替换用 REPLACE:
string(REPLACE "World" "CMake" NEW_TEXT "${TEXT}")
message("替换后: ${NEW_TEXT}") # Hello, CMake CMake!
嗯,这里要注意:REPLACE 是全局替换,不是只替换第一个。如果你只想替换一次,得用正则。
string(REPLACE "/" "\\" WIN_PATH "${UNIX_PATH}") 来转换路径分隔符。Windows 和 Linux 的路径格式不同,这个操作很实用。
6.2.2 字符串大小写转换
大小写转换用 TOLOWER 和 TOUPPER:
string(TOLOWER "Hello CMake" LOW)
string(TOUPPER "Hello CMake" UP)
message("小写: ${LOW}") # hello cmake
message("大写: ${UP}") # HELLO CMAKE
这个功能在比较字符串时特别有用。比如用户输入的选项可能是 "ON"、"on"、"On",统一转成大写再比较,省心很多。
6.2.3 字符串正则匹配
正则匹配是 string 命令里最强大的功能。CMake 支持基本的正则表达式语法:
set(VERSION "3.27.1")
string(REGEX MATCH "([0-9]+)\\.([0-9]+)" MATCHED "${VERSION}")
message("匹配结果: ${MATCHED}") # 3.27
string(REGEX MATCHALL "[0-9]+" ALL_NUMS "${VERSION}")
message("所有数字: ${ALL_NUMS}") # 3;27;1
正则替换也很常用:
string(REGEX REPLACE "([0-9]+)\\.([0-9]+)" "v\\1_\\2" NEW_VER "${VERSION}")
message("替换后: ${NEW_VER}") # v3_27.1
我在项目中遇到过最典型的场景:解析编译器版本号。GCC 的输出格式经常变,用正则提取主版本号、次版本号,比硬解析靠谱得多。
\\1、\\2 来引用捕获组。注意是双反斜杠,不是单反斜杠。这个细节坑过不少人。
6.3 字符串其他实用操作
除了上面这些,还有几个常用操作:
- LENGTH:获取字符串长度
- SUBSTRING:截取子串
- STRIP:去除首尾空白
- COMPARE:比较字符串
看个综合例子:
set(INPUT " CMake 3.27 ")
string(STRIP "${INPUT}" CLEAN_INPUT)
string(LENGTH "${CLEAN_INPUT}" LEN)
string(SUBSTRING "${CLEAN_INPUT}" 0 5 PREFIX)
message("清理后: '${CLEAN_INPUT}'") # 'CMake 3.27'
message("长度: ${LEN}") # 11
message("前缀: ${PREFIX}") # CMake
字符串比较时,我建议用 STREQUAL 而不是 EQUAL。前者是字符串比较,后者会尝试转成数字比较,容易出问题。
6.4 知识体系总览
下面这张图总结了本章的核心内容:
6.5 实战经验总结
最后分享几个我这些年积累的经验:
- 版本号比较:不要直接用字符串比较 "3.10" 和 "3.9"。我习惯用
string(REGEX MATCHALL)提取数字,然后逐个比较。 - 路径处理:用
string(REPLACE)转换路径分隔符时,注意 Windows 下反斜杠需要转义。 - 正则调试:CMake 的正则引擎比较弱,复杂正则建议先在在线工具上测试,再移植过来。
- 性能考虑:在循环里频繁调用
string命令会影响构建速度。能提前算好的值,尽量用变量缓存。
数学和字符串操作,是 CMake 构建脚本的基础能力。掌握好了,写出来的 CMakeLists.txt 会更健壮、更易维护。下一章咱们聊聊文件操作和路径处理,这些在实际项目中同样重要。
公众号:蓝海资料掘金营,微信deep3321