草庐IT

PHP 正则表达式和相邻的捕获组

codeneng 2023-03-28 原文

PHP regex and adjacent capturing groups

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

我正在尝试将 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

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

我做错了什么?

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


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

  • 您的正则表达式匹配 IsATest 中的 sA,允许您在 sA 之间插入 -
  • 为了在 AT 之间插入 -,正则表达式必须匹配 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"));

    比赛演示

    代码演示

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

    有关PHP 正则表达式和相邻的捕获组的更多相关文章

    1. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

      在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

    2. ruby - 正则表达式将非英文字母匹配为非单词字符 - 2

      @raw_array[i]=~/[\W]/非常简单的正则表达式。当我用一些非拉丁字母(具体来说是俄语)尝试时,条件是错误的。我能用它做什么? 最佳答案 @raw_array[i]=~/[\p{L}]/使用西里尔字符进行测试。引用:http://www.regular-expressions.info/unicode.html#prop 关于ruby-正则表达式将非英文字母匹配为非单词字符,我们在StackOverflow上找到一个类似的问题: https://

    3. ruby - 如何让Ruby捕获线程中的语法错误 - 2

      我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

    4. ruby - 正则表达式在哪个位置失败? - 2

      我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束

    5. ruby-on-rails - 无法在 Rails 助手中捕获 block 的输出 - 2

      我在使用自定义RailsFormBuilder时遇到了问题,从昨天晚上开始我就发疯了。基本上我想对我的构建器方法之一有一个可选block,以便我可以在我的主要content_tag中显示其他内容。:defform_field(method,&block)content_tag(:div,class:'field')doconcatlabel(method,"Label#{method}")concattext_field(method)capture(&block)ifblock_given?endend当我在我的一个Slim模板中调用该方法时,如下所示:=f.form_field:e

    6. ruby - 有没有办法从 ruby​​ case 语句中访问表达式? - 2

      我想从then子句中访问c​​ase语句表达式,即food="cheese"casefoodwhen"dip"then"carrotsticks"when"cheese"then"#{expr}crackers"else"mayo"end在这种情况下,expr是食物的当前值(value)。在这种情况下,我知道,我可以简单地访问变量food,但是在某些情况下,该值可能无法再访问(array.shift等)。除了将expr移出到局部变量然后访问它之外,是否有直接访问caseexpr值的方法?罗亚附注我知道这个具体示例很简单,只是一个示例场景。 最佳答案

    7. ruby - 正则表达式 - 排除一个字符 - 2

      这是一个例子:s="abcd+subtext@example.com"s.match(/+[^@]*/)Result=>"+subtext"问题是,我不想在其中包含“+”。我希望结果是“潜台词”,没有+ 最佳答案 您可以在正则表达式中使用括号来创建匹配组:s="abcd+subtext@example.com"s=~/\+([^@]*)/&&$1=>"subtext" 关于ruby-正则表达式-排除一个字符,我们在StackOverflow上找到一个类似的问题:

    8. ruby - 如何遍历 Ruby 中所有正则表达式匹配的字符串? - 2

      我们有一个字符串:“”这个正则表达式://i如何从当前字符串中获取所有匹配项? 最佳答案 "".scan(//)参见scan在ruby​​-docs上 关于ruby-如何遍历Ruby中所有正则表达式匹配的字符串?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/6857852/

    9. ruby - 在 ruby​​ 中生成一个进程,捕获 stdout,stderr,获取退出状态 - 2

      我想从ruby​​rake脚本运行一个可执行文件,比如foo.exe我希望将foo.exe的STDOUT和STDERR输出直接写入我正在运行rake任务的控制台.当进程完成时,我想将退出代码捕获到一个变量中。我如何实现这一目标?我一直在玩backticks、process.spawn、system但我无法获得我想要的所有行为,只有部分更新:我在Windows上,在标准命令提示符下,而不是cygwin 最佳答案 system获取您想要的STDOUT行为。它还返回true作为零退出代码,这可能很有用。$?填充了有关最后一次system调

    10. Ruby 正则表达式匹配逗号,但忽略括号中的逗号 - 2

      我正在尝试通过正则表达式拆分参数列表。这是一个带有我的参数列表的字符串:"a=b,c=3,d=[1,3,5,7],e,f=g"我想要的是:["a=b","c=3","d=[1,3,5,7]","e","f=g"]我试过先行,但Ruby不允许使用动态范围后行,所以这行不通:/(?如何让正则表达式忽略方括号中的所有内容? 最佳答案 也许这样的东西对你有用:str.scan(/(?:\[.*?\]|[^,])+/)编辑再三考虑。简单的非贪婪匹配器在某些嵌套括号的情况下会失败。 关于Ruby正则

    随机推荐