8

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

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

4

8 回答 8

12

使用负前瞻负后瞻。

这将是正则表达式:(.)(?<!\1.)\1{N-1}(?!\1)除了 Python 的 re 模块已损坏(请参阅此链接)。

英文翻译:“匹配任何字符。确保在匹配该字符之后,它之前的字符不是那个字符。匹配该字符的 N-1 个重复。确保那些重复之后的字符不是那个字符特点。”

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

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

这可以通过使用(?=\1)..而不是解决 re 模块中的错误\1.(这些在大多数情况下是等效的。)这让正则表达式引擎准确地知道后向断言的宽度,因此它可以在 PCRE 和 re 等中工作。


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

于 2012-04-25T16:57:59.137 回答
6

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

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

更新:

^(.)\\1{3}(?!\\1)|(.)(?<!(?=\\2)..)\\2{3}(?!\\2)更普遍地为我工作,包括从字符串开头开始的匹配。

于 2012-04-25T16:21:04.143 回答
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";
}

输出:

“xxaaaayybbbbbzzccccxx”:
  - [aaaa][cccc]
“aaaayybbbbbzzccccxx”:
  - [aaaa][cccc]
“xxaaaa”:
  - [aaaa]
“啊啊”:
  - [aaaa]
“”:
  - []

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

于 2012-04-25T17:35:07.377 回答
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
于 2012-04-25T17:02:12.030 回答
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']
于 2012-04-25T16:38:31.897 回答
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());
    }
}
于 2012-04-26T01:26:36.267 回答
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”]。然后第二个循环测试这些部分的长度。最后,该函数仅返回具有当前长度的部分。我不是最擅长解释代码的人,所以如果需要,任何人都可以自由地加强这个解释。

无论如何,我认为这会做!

于 2012-04-25T16:55:26.800 回答
1

为什么不让正则表达式引擎做它最擅长的事情——找到最长的相同符号字符串,然后自己检查长度?

在 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")
    }
}
于 2012-04-25T16:59:50.997 回答