如何使用正则表达式匹配字符串中的中间字符?

11
在一个奇数长度的字符串中,如何匹配(或捕获)中间字符?
使用PCRE、纯Perl或Java正则表达式风格是否可行?
在.NET正则表达式中,您可以使用平衡组轻松解决它(这可能是一个很好的例子)。通过纯Perl正则表达式,我的意思是不使用任何代码构造,比如(??{ ... }),您可以在其中运行任何代码并且当然可以进行任何操作。
该字符串的长度可以是任何奇数。
例如,在字符串12345中,您将想要获取3,即字符串中心的字符。
这是关于现代正则表达式风格的可能性而不是其他方式中最佳算法的问题。

6
Perl: 可以。使用递归或/^(.*)(.)(??{ '.' x length($1) })\z/s - ikegami
1
@Oli,那不是真的。只有使用真正的正则表达式才是不可能的,但OP明确表示他知道这一点,并且他正在谈论某些语言实现的正则表达式引擎。 - ikegami
1
@Qtax,“/…/”是一个正则表达式匹配运算符。//之间的所有内容都是Perl正则表达式。至于递归,我指的是正则表达式递归,而不是Perl递归。 - ikegami
1
@ikegami,你可以在(??{ ... })和类似的地方嵌入任何代码,以实现任何功能。我应该删除Perl标签还是让它更清晰明了? - Qtax
2
如果你想询问有关PCRE和Java的问题,但不关心Perl,请继续。虽然这有点奇怪,要询问现代正则表达式引擎可以做什么,但并不想知道领先者能做什么。 - ikegami
显示剩余6条评论
2个回答

8

使用 PCRE、Perl(可能还有 Java)可以使用以下代码:

^(?:.(?=.*?(?(1)(?=.\1$))(.\1?$)))*(.)

这将在第二个捕获组中捕获奇数长度字符串的中间字符。

解释

^ # beginning of the string
(?: # loop
  . # match a single character
  (?=
    # non-greedy lookahead to towards the end of string
    .*?
    # if we already have captured the end of the string (skip the first iteration)
    (?(1)
      # make sure we do not go past the correct position
      (?= .\1$ )
    )
    # capture the end of the string +1 character, adding to \1 every iteration
    ( .\1?$ )
  )
)* # repeat
# the middle character follows, capture it
(.)

2
我发现这个:^(?:.(?=.*((?(1).\1|.))$))*\K. 并没有太大的区别。 - Casimir et Hippolyte
3
或者带有正确量词的表达式:^(?:.(?=.+((?(1).\1|.))$))*\K. - Casimir et Hippolyte
2
@CasimiretHippolyte,不错的解决方案,点赞!你应该发布它。 - Qtax
1
@CasimiretHippolyte 也许今天就是那一天? :) 你们很接近了,但是可以通过使这些量词懒惰匹配并给它们一个停止的位置来避免条件:^(?:.(?=.*?(.\1?$)))+?\K.(?=\1) - jaytea
3
@jaytea:你也接近了,^(?:.(?=.*?(.\1?$)))*?\K.(?=\1?$) - Casimir et Hippolyte
显示剩余2条评论

2

嗯,也许有人能想出完全的正则表达式解决方案,但如果没有,你总可以像这样动态构建正则表达式:

public static void main(String[] args) throws Exception {
    String s = "12345";
    String regex = String.format(".{%d}3.{%d}", s.length() / 2, s.length() / 2);
    Pattern p = Pattern.compile(regex);
    System.out.println(p.matcher(s).matches());
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接