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

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

1 HelloWorldThisIsATest => hello world this is a test

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

1 mb_strtolower ( preg_replace ( ‘/([A-Za-z])([A-Z])/’ , ‘$1-$2’ , “HelloWorldThisIsATest” ) ) ;

结果:

1 hello world this is atest

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

我做错了什么?



相关讨论

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


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

  • 您的正则表达式匹配 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 匹配。

参考

  • 前瞻和后瞻零长度断言
  • 掌握前瞻和后瞻



相关讨论

  • 您的正则表达式无法工作的原因是它需要允许连续匹配。稍后将对此进行解释。您必须像我的回答一样使用环视。
  • 你的问题说 I’m wondering what my problem is … 我的回答实际上提供了一个解释。
  • 我也喜欢你的回答,因为它解释了为什么我的正则表达式不起作用。
  • 您的正则表达式将仅匹配两个字母。
  • @AvinashRaj 查看我的演示。 :)
  • @AvinashRaj 谢谢,我知道你是一名正则表达式学者——这是解决这类问题的最佳解决方案(零宽度匹配。)
  • 谢谢林克,下次见。 :)


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

1 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

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

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



    相关讨论

    • +1 ,好清晰的解决方案,也解决了多个大写缩写的用例
    • 我们确定没有因为角色安排而不需要三遍的情况吗?……或者更多……?我有一种预感,可能就是这种情况。
    • 1 因为它涵盖了将来可能适用于我的用例,也是一个很好的解决方案
    • @zx81 虽然在技术上是可行的,但您使用的首字母缩略词将完全以大写字母命名。
    • @rink.attendant.6 顺便说一句,我已经设法将它全部压缩到一个正则表达式中:)
    • 嗯,不太确定…在我看来,第一次通过时,字符串被 – 的任意数字扩展,这可能是奇数或偶数…因此,在第一次通过时遗漏了两个位置最终可能会得到不同的平价。但是我的直觉可能是错误的,我并没有停下来在纸上解决它。
    • @zx81 当你有的时候让我;我很想看看是否有”失败”的案例。
    • 嘿,杰克,IMO 最好的确定方法是对具有随机序列的文件进行测试。没有时间,但我拿了一篇维基百科文章,删除了 [^a-zA-Z]+ 并运行了两个替换。如你所料,剩下的大写字母还不少,一个也没有。值得尝试更大的样本,因为在普通文本中没有那么多大小写切换。下次见! :)


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

    1 (? <= [a z ] ) (? = [A Z ] ) | (? <= [A Z ] ) (? = [A Z ] [a z ] )

    你可以一分为二来解释:

    第一部分

    1
    2
    3
    4
    5
    6
    (? <=                     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 的字符串之间的匹配。)

    第二部分

    1
    2
    3
    4
    5
    6
    7
    (? <=                     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: 特殊情况,缩写和驼峰模式匹配)

    所以你的代码是:

    1 mb_strtolower ( preg_replace ( ‘/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/’ , ‘-‘ , “HelloWorldThisIsATest” ) ) ;

    比赛演示

    代码演示



    相关讨论

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


    声明:本站(华域联盟www.cnhackhy.com)所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。