
preg_match_all 提取字符串中所有数字
PHP 里最可靠、最常用的方法就是用 preg_match_all 配合正则匹配数字。别用 str_split 或循环判断 ASCII,既慢又漏负数、小数、科学计数法。
常见错误现象:只匹配整数却漏掉 "-12.34" 或 "1e5";或用 is_numeric() 判断单个字符,结果把 "."、"e" 当成数字。
- 提取所有连续数字(含负号和小数点):
preg_match_all('/-?\d+\.?\d*/', $str, $matches),但注意这会把"12.34.56"拆成["12.34", "56"] - 更严谨匹配浮点数(推荐):
preg_match_all('/-?\d+\.?\d*(?:[eE][+-]?\d+)?/', $str, $matches),能覆盖"-3.14e-2" - 如果只要整数(不含小数、指数),用
/'-?\d+'/'即可,性能略好
filter_var + FILTER_SANITIZE_NUMBER_INT / FLOAT 的坑
filter_var 看起来简单,但实际不适合“提取”,它本质是“清洗”——把非数字字符全删掉,再拼一起,容易串号。
使用场景:你只是想快速清理用户输入的电话、价格字段,不关心原始位置或分组。
10分钟内自己学会PHP其中,第1篇为入门篇,主要包括了解PHP、PHP开发环境搭建、PHP开发基础、PHP流程控制语句、函数、字符串操作、正则表达式、PHP数组、PHP与Web页面交互、日期和时间等内容;第2篇为提高篇,主要包括MySQL数据库设计、PHP操作MySQL数据库、Cookie和Session、图形图像处理技术、文件和目录处理技术、面向对象、PDO数据库抽象层、程序调试与错误处理、A
立即学习“PHP免费学习笔记(深入)”;
-
filter_var($str, FILTER_SANITIZE_NUMBER_INT)会删掉所有非数字和负号,"abc-12def34"→"-1234"(不是["-12", "34"]) -
FILTER_SANITIZE_NUMBER_FLOAT还会保留小数点和e,但同样不区分字段边界,"x1.2y3.4z"→"1.23.4",明显错 - 它不返回数组,没法知道数字原来在哪儿,也不支持自定义分隔逻辑
intval / floatval 在字符串开头有数字时的行为
这两个函数不是提取工具,而是类型转换函数——它们只解析字符串**开头**的数字部分,遇到第一个非数字就停。
容易踩的坑:以为 intval("abc123") 能拿到 123,结果是 0;而 intval("123abc") 是 123,看起来对,但完全忽略后面可能还有数字。
-
intval("42px")→42,但intval("px42")→0 -
floatval("3.14abc")→3.14,floatval("abc3.14")→0.0 - 它们无法处理多个独立数字,也不报错,静默失败,调试时很难发现
性能与兼容性提醒
正则方案在 PHP 7.4+ 和 8.x 下表现稳定,preg_match_all 比循环 + ctype_digit 快 3–5 倍(尤其长字符串)。但要注意 PCRE JIT 编译开销——如果正则固定,建议提前编译(preg_replace_callback 不适用,但 preg_match_all 可缓存 pattern)。
- PHP 8.2+ 开始,
/u修饰符对纯 ASCII 数字无影响,但若字符串含 Unicode 数字(如阿拉伯数字),需显式加u并用\p{N} - 避免用
mb_ereg,已废弃且慢;也别用strtok配合is_numeric,逻辑复杂还易出错 - 真正难的不是“怎么拿数字”,而是“怎么定义你要的数字”——要不要负号?是否接受千分位逗号?是否允许空格分隔?这些决定了正则怎么写,而不是换函数










