
本文介绍如何用 preg_match_all 配合恰当的正则模式,从形如 "7 years 11 months 3 weeks" 的字符串中准确提取带单位的时间片段,并支持零值与浮点数扩展。
本文介绍如何用 `preg_match_all` 配合恰当的正则模式,从形如 `"7 years 11 months 3 weeks"` 的字符串中准确提取带单位的时间片段,并支持零值与浮点数扩展。
在处理自然语言描述的时间跨度(如用户输入的年龄、工期或周期)时,常见格式为 "X years Y months Z weeks"。若目标是将每个“数值+单位”组合(如 "7 years"、"11 months")独立提取为数组元素,不应使用 preg_split 进行分割——因为该函数以分隔符为界切分字符串,而本例中分隔符(空格)恰恰是各片段内部的组成部分,强行用空格分割会导致 "7"、"years" 被错误拆开。
正确思路是匹配而非分割:直接捕获所有符合“数字 + 空格 + 单词”结构的连续子串。PHP 中推荐使用 preg_match_all(),它能一次性提取全部匹配项,并天然返回结构化结果。
✅ 推荐正则方案
$text = "7 years 11 months 3 weeks";
preg_match_all('/\d+\s+\w+/', $text, $matches);
// $matches[0] 即为所需结果:
// Array (
// [0] => 7 years
// [1] => 11 months
// [2] => 3 weeks
// )
- \d+:匹配一个或多个数字(支持 0,如 "0 years");
- \s+:匹配一个或多个空白字符(兼容空格、制表符等);
- \w+:匹配一个或多个单词字符(字母、数字、下划线),适用于 years、months、weeks 等标准单位。
? 为什么不用 preg_split?
原问题中尝试的 (?!\d+)(?:\s)(?!\D+) 混淆了否定先行断言的语义,且试图用空格作分隔符,必然破坏 "7 years" 的完整性。正则设计应遵循“匹配目标内容”,而非“避开什么”。
? 进阶用法:分离数值与单位
若后续需分别处理数字和单位(例如转为秒数、做单位归一化),可添加捕获组:
preg_match_all('/(\d+)\s+(\w+)/', $text, $matches, PREG_SET_ORDER);
// $matches 结构示例:
// Array (
// [0] => Array ( [0] => 7 years [1] => 7 [2] => years )
// [1] => Array ( [0] => 11 months [1] => 11 [2] => months )
// [2] => Array ( [0] => 3 weeks [1] => 3 [2] => weeks )
// )
此时 $matches[i][1] 是数值,$matches[i][2] 是单位,便于循环处理。
⚠️ 注意事项与扩展建议
-
兼容浮点数:若输入可能含小数(如 "1.5 years"),将 \d+ 替换为 \d*\.?\d+(注意需转义点号):
\d*\.?\d+\s+\S+
- 支持连字符/特殊单位:用 \S+ 替代 \w+ 可匹配 man-hours、day-to-day 等含连字符的单位。
-
严格单位校验(可选):若仅接受 years/months/weeks,可限定为 (years|months|weeks),提升健壮性:
\d+\s+(years|months|weeks)
- 空字符串/异常输入:调用前建议 trim($text) 并检查 !empty($text),避免空匹配。
通过匹配而非分割的思维转换,配合语义清晰的正则模式,即可稳定、高效地解析各类时间片段字符串——这是处理自然语言结构化数据的关键实践。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











