恰好匹配相同字符的N次重复

时间:2012-04-25 16:19:22

标签: java .net python regex perl

如何编写一个完全匹配相同字符(或理想情况下,同一组)的N次重复的表达式?基本上,(.)\1{N-1}做了什么,但有一个重要的限制:如果主题重复更多而不是N次,则表达式应该失败。例如,给定N=4和字符串xxaaaayyybbbbbzzccccxx,表达式应与aaaacccc匹配,而不是bbbb

我不专注于任何特定的方言,随意使用任何语言。请不要发布仅适用于此特定示例的代码,我正在寻找一般解决方案。

8 个答案:

答案 0 :(得分:11)

使用否定前瞻否定后视。

这将是正则表达式:(.)(?<!\1.)\1{N-1}(?!\1),除了Python的re模块被破坏(参见this link)。

英语翻译:“匹配任何一个角色。确保在匹配该角色后,前面的角色也不是那个角色。再匹配N-1个角色的重复次数。确保重复之后的角色是不是那个角色。“

不幸的是,re模块(和大多数正则表达式引擎)都被破坏了,因为你不能在lookbehind断言中使用反向引用。 Lookbehind断言需要是恒定长度,并且编译器不够聪明,无法推断它是在使用反向引用时(即使在这种情况下,backref具有恒定长度)。我们必须通过这个来处理正则表达式编译器,如下所示:

实际答案必须更加混乱:r"(.)(?<!(?=\1)..)\1{N-1}(?!\1)"

这可以通过使用(?=\1)..代替\1.来解决re模块中的错误(这些错误在大多数情况下是等效的。)这使得正则表达式引擎可以准确地知道后瞻断言的宽度,所以它适用于PCRE和重复等等。


当然,现实世界的解决方案就像[x.group() for x in re.finditer(r"(.)\1*", "xxaaaayyybbbbbzzccccxx") if len(x.group()) == 4]

答案 1 :(得分:6)

我怀疑你想要使用否定前瞻:(.)\1{N-1}(?!\1)

但是那说......我怀疑最简单的跨语言解决方案只是自己编写而不使用正则表达式。

更新:

^(.)\\1{3}(?!\\1)|(.)(?<!(?=\\2)..)\\2{3}(?!\\2)更适用于我,包括从字符串开头开始的匹配。

答案 2 :(得分:2)

很容易对正则表达式施加太多负担并尝试让他们做所有,只需几乎一切都会做到!

使用正则表达式查找由单个字符组成的所有子字符串,然后分别检查它们的长度,如下所示:

use strict;
use warnings;

my $str = 'xxaaaayyybbbbbzzccccxx';

while ( $str =~ /((.)\2*)/g ) {
  next unless length $1 == 4;
  my $substr = $1;
  print "$substr\n";
}

<强>输出

aaaa
cccc

答案 3 :(得分:2)

Perl的正则表达式引擎不支持可变长度的lookbehind,所以我们必须慎重考虑它。

sub runs_of_length {
  my($n,$str) = @_;

  my $n_minus_1 = $n - 1;
  my $_run_pattern = qr/
    (?:
       # In the middle of the string, we have to force the
       # run being matched to start on a new character.
       # Otherwise, the regex engine will give a false positive
       # by starting in the middle of a run.
       (.) ((?!\1).) (\2{$n_minus_1}) (?!\2) |
       #$1 $2        $3

       # Don't forget about a potential run that starts at
       # the front of the target string.
           ^(.)      (\4{$n_minus_1}) (?!\4)
       #    $4       $5
    )
  /x;

  my @runs;
  while ($str =~ /$_run_pattern/g) {
    push @runs, defined $4 ? "$4$5" : "$2$3";
  }

  @runs;
}

一些测试用例:

my @tests = (
  "xxaaaayyybbbbbzzccccxx",
    "aaaayyybbbbbzzccccxx",
  "xxaaaa",
    "aaaa",
  "",
);

$" = "][";
for (@tests) {
  my @runs = runs_of_length 4, $_;
  print qq<"$_":\n>,
        "  - [@runs]\n";
}

输出:

"xxaaaayyybbbbbzzccccxx":
  - [aaaa][cccc]
"aaaayyybbbbbzzccccxx":
  - [aaaa][cccc]
"xxaaaa":
  - [aaaa]
"aaaa":
  - [aaaa]
"":
  - []

这是一个有趣的谜题,但是如果这样的结构出现在生产代码中,你的正则表达式的同事可能会感到不快。

答案 4 :(得分:1)

>>> import itertools
>>> zz = 'xxaaaayyybbbbbzzccccxxaa'
>>> z = [''.join(grp) for key, grp in itertools.groupby(zz)]  
>>> z  
['xx', 'aaaa', 'yyy', 'bbbbb', 'zz', 'cccc', 'xx', 'aa']

从那里你可以遍历列表并检查N==4非常容易的场合,如下所示:

>>> [item for item in z if len(item)==4]
['cccc', 'aaaa']

答案 5 :(得分:1)

在python中这个怎么样?

def match(string, n):
    parts = []
    current = None
    for c in string:
        if not current:
            current = c
        else:
            if c == current[-1]:
                current += c
            else:
                parts.append(current)
                current = c

    result = []
    for part in parts:
        if len(part) == n:
            result.append(part)

    return result

使用各种尺寸的字符串进行测试:

match("xxaaaayyybbbbbzzccccxx", 6) = []
match("xxaaaayyybbbbbzzccccxx", 5) = ["bbbbb"]
match("xxaaaayyybbbbbzzccccxx", 4) = ['aaaa', 'cccc']
match("xxaaaayyybbbbbzzccccxx", 3) = ["yyy"]
match("xxaaaayyybbbbbzzccccxx", 2) = ['xx', 'zz']

说明:

第一个循环基本上将文本分成若干部分,如:[“xx”,“aaaa”,“yyy”,“bbbbb”,“zz”,“cccc”,“xx”]。然后第二个循环测试这些部分的长度。最后,该函数仅返回具有当前长度的部分。我不是最擅长解释代码的,所以如果需要,任何人都可以自由地增强这种解释。

无论如何,我认为这样做了!

答案 6 :(得分:1)

为什么不给regexp引擎留下最好的东西 - 找到最长的相同符号串然后自己检查长度?

Perl:

my $str = 'xxaaaayyybbbbbzzccccxx';

while($str =~ /(.)\1{3,}/g){
    if(($+[0] - $-[0]) == 4){ # insert here full match length counting specific to language
        print (($1 x 4), "\n")
    }
}

答案 7 :(得分:1)

在Java中,我们可以像下面的代码那样做

String test ="xxaaaayyybbbbbzzccccxx  uuuuuutttttttt";

int trimLegth = 4; // length of the same characters

Pattern p = Pattern.compile("(\\w)\\1+",Pattern.CASE_INSENSITIVE| Pattern.MULTILINE);

Matcher m = p.matcher(test);
while (m.find())
{ 
    if(m.group().length()==trimLegth) {
        System.out.println("Same Characters String " + m.group());
    }
}