PHP regex and adjacent capturing groups
我第一次在正则表达式中使用捕获组,我想知道我的问题是什么,因为我假设正则表达式引擎从左到右查看字符串。
我正在尝试将 UpperCamelCase 字符串转换为连字符小写字符串,例如:
1 | HelloWorldThisIsATest => hello-world-this-is-a-test |
我的前提是一个字母字符串,所以我不需要担心数字或其他字符。这是我尝试过的:
1 |
结果:
1 | hello-world-this-is-atest |
这几乎是我想要的,除了
我做错了什么?
你的正则表达式不起作用的原因:重叠匹配
-
您的正则表达式匹配
IsATest 中的sA ,允许您在s 和A 之间插入- -
为了在
A 和T 之间插入- ,正则表达式必须匹配AT 。 -
这是不可能的,因为
A 已经作为sA 的一部分进行了匹配。在直接正则表达式中不能有重叠匹配。 - 所有的希望都失去了吗?不!这是环视的完美情况。
用两条简单的线做到这一点
这是使用正则表达式的简单方法:
1 2 | $regex = '~(?<=[a-zA-Z])(?=[A-Z])~'; echo strtolower(preg_replace($regex,"-","HelloWorldThisIsATest")); |
查看 php 演示底部的输出:
Output:
hello-world-this-is-a-test
稍后会添加解释。 :)
- 正则表达式不匹配任何字符。相反,它针对字符串中的位置:字母大小写变化之间的位置。为此,它使用后视和前瞻
-
(?<=[a-zA-Z]) 后视断言当前位置之前是一个字母 -
(?=[A-Z]) 前瞻断言当前位置后面是一个大写字母。 -
我们只是用
- 替换这些位置,并将手数转换为小写。
如果您仔细查看此 regex101 屏幕,您会看到单词之间的线条,其中 regex 匹配。
参考
- 前瞻和后瞻零长度断言
- 掌握前瞻和后瞻
为简单起见,我将两个正则表达式分开:
1 |
它处理字符串两次以找到:
这将具有以下行为:
1 2 | ThisIsHTMLTest -> This-Is-HTML-Test ThisIsATest -> This-Is-A-Test |
或者,使用前瞻断言(这将影响上一次匹配中使用的最后一个大写字母的重用):
1 |
为了修复 Jack 在您的评论中提到的有趣用例(避免缩写词的拆分),我采用了 zx81 的使用前瞻和后瞻的路线。
1 | (?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z]) |
你可以一分为二来解释:
第一部分
1 2 3 4 5 6 |
(TL;DR: CamelCase Pattern 的字符串之间的匹配。)
第二部分
1 2 3 4 5 6 7 |
(TL;DR: 特殊情况,缩写和驼峰模式匹配)
所以你的代码是:
1 | mb_strtolower(preg_replace('/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/', '-',"HelloWorldThisIsATest")); |
比赛演示
代码演示