我第一次在正则表达式中使用捕获组,我想知道我的问题是什么,因为我假设正则表达式引擎从左到右查看字符串。
我正在尝试将UpperCamelCase字符串转换为hyphened-lowercase-string,例如:
HelloWorldThisIsATest => hello-world-this-is-a-test
我的前提条件是字母字符串,所以我不需要担心数字或其他字符。这是我试过的:
mb_strtolower(preg_replace('/([A-Za-z])([A-Z])/', '$1-$2', "HelloWorldThisIsATest"));
结果:
hello-world-this-is-atest
这几乎是我想要的,除了a
和test
之间应该有一个连字符。我已经将A-Z
包含在我的第一个捕获组中,因此我假设引擎看到AT
并将其连字。
我做错了什么?
答案 0 :(得分:6)
正则表达式不起作用的原因:重叠匹配
sA
中的IsATest
匹配,允许您在-
和s
之间插入A
-
和A
之间插入T
,正则表达式必须与AT
匹配。 A
已作为sA
的一部分进行匹配。您不能在直接正则表达式中重叠匹配。以两条简单的方式进行
以下是使用正则表达式执行此操作的简便方法:
$regex = '~(?<=[a-zA-Z])(?=[A-Z])~';
echo strtolower(preg_replace($regex,"-","HelloWorldThisIsATest"));
请参阅php demo:
底部的输出输出:
hello-world-this-is-a-test
稍后会添加解释。 :)
(?<=[a-zA-Z])
lookbehind断言当前位置前面的字母是(?=[A-Z])
前瞻断言当前位置后面的内容是一个大写字母。-
替换这些位置,并将该地块转换为小写。如果仔细查看regex101 screen,可以看到正则表达式匹配的单词之间的行。
<强>参考强>
答案 1 :(得分:5)
为简单起见,我将两个正则表达式分开了:
preg_replace(array('/([a-z])([A-Z])/', '/([A-Z]+)([A-Z])/'), '$1-$2', $string);
它处理字符串两次以找到:
这将有以下行为:
ThisIsHTMLTest -> This-Is-HTML-Test
ThisIsATest -> This-Is-A-Test
或者,使用前瞻断言(这将影响上一次匹配中使用的最后一个大写字母的重用):
preg_replace('/([A-Z]+|[a-z]+)(?=[A-Z])/', '$1-', $string);
答案 2 :(得分:4)
为了修复Jack在你的评论中提到的有趣用例(避免拆分缩写),我选择了zx81使用lookahead和lookbehinds的路线。
(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])
您可以将其拆分为两个以进行解释:
(?<= look behind to see if there is:
[a-z] any character of: 'a' to 'z'
) end of look-behind
(?= look ahead to see if there is:
[A-Z] any character of: 'A' to 'Z'
) end of look-ahead
(TL; DR:CamelCase模式的字符串之间的匹配。)
(?<= look behind to see if there is:
[A-Z] any character of: 'A' to 'Z'
) end of look-behind
(?= look ahead to see if there is:
[A-Z] any character of: 'A' to 'Z'
[a-z] any character of: 'a' to 'z'
) end of look-ahead
(TL; DR:特例,缩写与CamelCase模式匹配)
那么你的代码就是:
mb_strtolower(preg_replace('/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/', '-', "HelloWorldThisIsATest"));