【问题标题】:PHP regex and adjacent capturing groupsPHP 正则表达式和相邻的捕获组
【发布时间】:2014-08-13 03:15:19
【问题描述】:

我第一次在正则表达式中使用捕获组,我想知道我的问题是什么,因为我假设正则表达式引擎从左到右查看字符串。

我正在尝试将 UpperCamelCase 字符串转换为连字符小写字符串,例如:

HelloWorldThisIsATest => hello-world-this-is-a-test

我的前置条件是一个字母字符串,所以我不需要担心数字或其他字符。这是我尝试过的:

mb_strtolower(preg_replace('/([A-Za-z])([A-Z])/', '$1-$2', "HelloWorldThisIsATest"));

结果:

hello-world-this-is-atest

这几乎是我想要的,除了atest 之间应该有一个连字符。我已经将A-Z 包含在我的第一个捕获组中,因此我假设引擎会看到AT 并将其连字符连接起来。

我做错了什么?

【问题讨论】:

  • "HelloWorldHTMLTest" 怎么样?应该变成"hello-world-html-test" 还是"hello-world-h-t-m-l-test"
  • @Jack 有趣的用例我没想到……我会说第一个。

标签: php regex string backreference camelcasing


【解决方案1】:

为了解决 Jack 在您的 cmets 中提到的有趣用例(避免缩写词的拆分),我采用了 zx81 使用前瞻和后瞻的路线。

(?<=[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 Pattern 的字符串之间的匹配。)

第二部分

(?<=                     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:特殊情况,缩写与驼峰模式匹配)

因此您的代码将是:

mb_strtolower(preg_replace('/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/', '-', "HelloWorldThisIsATest"));

Demo of matches

Demo of code

【讨论】:

  • 我刚想评论要求解释,就看到帖子被编辑了!谢谢,这就是我要找的。我会在几分钟后接受答案。
  • 它们都是很好的答案,但我不得不接受另一个,因为它为我的问题的第一部分提供了解释。
【解决方案2】:

为简单起见,我将两个正则表达式分开:

preg_replace(array('/([a-z])([A-Z])/', '/([A-Z]+)([A-Z])/'), '$1-$2', $string);

它处理字符串两次以找到:

  1. 小写 -> 大写边界
  2. 多个大写字母后跟另一个大写字母

这将具有以下行为:

ThisIsHTMLTest -> This-Is-HTML-Test
ThisIsATest    -> This-Is-A-Test

或者,使用前瞻断言(这将影响上一场比赛中使用的最后一个大写字母的重用):

preg_replace('/([A-Z]+|[a-z]+)(?=[A-Z])/', '$1-', $string);

【讨论】:

  • +1,好清晰的解决方案,也解决了多个大写缩写的用例
  • 我们确定没有由于字符排列而不需要三遍的情况吗?...或者更多...?我有一种预感,可能就是这种情况。
  • +1,因为它涵盖了将来可能适用于我的用例,也是一个很好的解决方案
  • @zx81 虽然在技术上是可行的,但您使用的首字母缩略词将完全以大写字母命名。
  • @rink.attendant.6 顺便说一句,我已经设法将它全部压缩到一个正则表达式中:)
【解决方案3】:

您的正则表达式不起作用的原因:重叠匹配

  • 您的正则表达式匹配IsATest 中的sA,允许您在sA 之间插入-
  • 为了在AT 之间插入-,正则表达式必须匹配AT
  • 这是不可能的,因为A 已经作为sA 的一部分进行匹配。在直接正则表达式中不能有重叠匹配。
  • 是不是所有的希望都落空了?不! 这是一个完美的环视情况。

用两条简单的线来做

这是使用正则表达式的简单方法:

$regex = '~(?<=[a-zA-Z])(?=[A-Z])~';
echo strtolower(preg_replace($regex,"-","HelloWorldThisIsATest"));

查看php demo底部的输出:

输出:hello-world-this-is-a-test

稍后会添加解释。 :)

  • 正则表达式不匹配任何字符。相反,它针对字符串中的位置:字母大小写变化之间的位置。为此,它使用后向和前瞻
  • (?&lt;=[a-zA-Z])lookbehind 断言当前位置之前是一个字母
  • (?=[A-Z]) 前瞻断言当前位置后面是一个大写字母。
  • 我们只需将这些位置替换为-,并将批次转换为小写。

如果您仔细查看此regex101 screen,您会看到单词之间的线条,其中正则表达式匹配。

参考

【讨论】:

  • 您的正则表达式无法工作的原因是它需要允许连续匹配。稍后将对此进行解释。您必须按照我的回答使用环视。
  • 你的问题说I'm wondering what my problem is...我的回答实际上提供了一个解释。
  • 我也喜欢你的回答,因为它解释了为什么我的正则表达式不起作用。
  • 您的正则表达式将只匹配两个字母。
  • @AvinashRaj 查看我的演示。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-21
  • 2015-09-11
  • 2016-02-10
  • 1970-01-01
  • 2018-03-11
  • 1970-01-01
相关资源
最近更新 更多