PHP实现字符串数字按序替换为序号占位符的正则方案
问题核心
原有实现使用str_replace做替换时,是对字符串做全局子串匹配,不会识别完整数字块,会把长数字内部包含的短数字字符也替换掉,比如替换0时会把64000里的三个0也误替换。靠替换数组按长度/数值排序的方案存在边界缺陷,遇到数字间存在包含关系(比如同时存在12和2、123)时仍可能出错。
最优实现方案
使用preg_replace_callback配合数字-占位符映射表实现,正则\d+天然匹配完整连续数字块,从根本上避免子串误替换问题,同时天然支持相同数字复用同一个占位符。
完整实现代码
$text1 = "S0 64000"; $text2 = "C0 64000"; $numPattern = '/\d+/'; // 提取基准文本中的所有完整数字块 preg_match_all($numPattern, $text1, $originalNums); // 构建数字到占位符的映射,相同数字仅分配一次占位符序号 $numPlaceholderMap = []; $index = 0; foreach ($originalNums[0] as $numStr) { if (!isset($numPlaceholderMap[$numStr])) { $numPlaceholderMap[$numStr] = "%d{$index}%"; $index++; } } // 校验两个待处理文本的数字集合是否一致 preg_match_all($numPattern, $text2, $transNums); $uniqueOriginal = array_unique($originalNums[0]); $uniqueTrans = array_unique($transNums[0]); sort($uniqueOriginal); sort($uniqueTrans); if ($uniqueOriginal !== $uniqueTrans) { dd("两个文本的数字集合不匹配"); } // 执行正则回调替换,每次匹配完整数字块后从映射表取对应占位符 $newText1 = preg_replace_callback( $numPattern, fn($match) => $numPlaceholderMap[$match[0]], $text1 ); $newText2 = preg_replace_callback( $numPattern, fn($match) => $numPlaceholderMap[$match[0]], $text2 );
方案优势
- 无替换错误:正则匹配逻辑只会识别连续的完整数字串,不会把长数字拆分为单个字符/短子串替换,完全规避
str_replace的误替换问题 - 无排序依赖:不需要对替换目标做长度、数值排序,不存在排序逻辑带来的边界bug
- 逻辑简洁:通过关联数组映射天然实现相同数字复用同一占位符的需求,符合规则要求
- 兼容性强:支持任意长度文本、任意位置的数字匹配,文本无数字时直接返回原内容,不需要额外特殊判断
场景验证
- 输入
"S0 64000",输出"S%d0% %d1%",符合预期 - 输入
"S0 0",输出"S%d0% %d0%",符合同数字复用占位符的要求 - 输入
"S10 455"/"C10 455",输出均为对应前缀加%d0% %d1%,满足后续占位符还原数值的需求
内容的提问来源于stack exchange,提问作者Stefano Martini
相关产品推荐
相关产品推荐

