在一个字符串中,如何使用Perl正则表达式查找第一个不连续重复的字符?

3
如果你接受这个任务,你需要编写一个Perl正则表达式,对于给定的字符串,它将返回第一个不连续重复的字符。换句话说,该字符前后都有与其不同的字符(或者分别是字符串的开头/结尾)。
例如:
IN: aabbcdecc
OUT: c

请注意,“not consecutively duplicated”并不意味着“在字符串的任何位置都不重复”。
注意:它必须是一个纯正则表达式。例如,显然会想到的解决方案(克隆字符串,删除所有重复项,并打印第一个剩余字符)不算数,尽管它解决了问题。
这个问题是受到我有些离题的回答的启发:如何使用Perl找到字符串中第一个不重复的字符?
3个回答

2
(?:(.)\1+)*(.?)

获取第二个捕获组。(如果每个字符都连续重复,则返回空字符串。)

测试用例:

~:2434$ perl -e "\"abc\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
a
~:2435$ perl -e "\"aabbcc\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"

~:2436$ perl -e "\"aabbc\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
c
~:2437$ perl -e "\"aabcc\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
b
~:2438$ perl -e "\"aabcbbbcccccc\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
b
~:2439$ perl -e "\"aabbvbbcccccc\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
v
~:2440$ perl -e "\"aabbcdecc\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
c
~:2441$ perl -e "\"aabbccddeef\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
f
~:2442$ perl -e "\"faabbccddeef\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
f
~:2443$ perl -e "\"faabbccddeefax\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
f
~:2444$ perl -e "\"xfaabbccddeefx\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
x
~:2445$ perl -e "\"xabcdefghai\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
x
~:2446$ perl -e "\"cccdddeeea12345\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
a
~:2447$ perl -e "\"1234a5678a23\" =~ m/(?:(.)\1+)*(.?)/; print \$2;"
1

或者(如果每个字符连续重复,则不匹配。)
(?:^|(.)(?!\1))(.)(?!\2)

1
不起作用 ;(。捕获的是"a"和"b"。我会留给你去弄清楚为什么 :) - DVK
1
谁给这个点赞了,请取消点赞 - 它不起作用! - DVK
@Kenny - 第二次尝试要好得多。你很快就发现了错误。我自己已经做到这一步了,但我还卡在“如果一开始没有重复怎么办”的问题上。 - DVK
@DVK: perl -e "\"aabbcdecc\" =~ m/(?:(.)\1+)*(.?)/; print \$2;" 输出 c; @spong perl -e "\"abcdefg\" =~ m/(?:(.)\1+)*(.?)/; print \$2;" 输出 a。我不知道为什么你们会出错。 - kennytm
@kenny - 抱歉,我正在测试你的原始表达式 (.)。 - DVK
显示剩余4条评论

1
use 5.010;
$str=~/^(([a-z])\g{-1}+)*(?<c>[a-z])/i;
$char = $+{c};

错误。[a-z]{2,}将匹配abc - kennytm
错误:正则表达式中未识别序列(?<c...)。 - DVK
@DVK:那是5.10标记的捕获功能,以及\g相对反向引用。 - brian d foy
@brian - 像往常一样发人深省。我对5.10稍加关注,但在工作中,我仍然被困在晚侏罗纪的5.8和零星的5.005 呕吐 :( - DVK

0

我希望Perl有一个正则表达式否定标志!即,返回所有不匹配/regex/的字符。

你真正需要的是正则表达式捕获补集:

m/(.)(\1)+/

我尝试了这个页面上针对Brian的数据列表(即他程序清单中的结果)的所有建议。但是没有一个完全有效。

正则表达式为:

(?:^|(.)(?!\1))(.)(?!\2) 

第2行和第3行的开头未能匹配'f'。Brian的未能匹配第2行和第3行开头的'f'或第5行末尾的任何单个字符。

正则表达式为:

$str=~/^(([a-z])\g{-1}+)*(?<c>[a-z])/i;
$char = $+{c};

有效。

我找到的唯一一个正则表达式是一个简单的:

#!/usr/bin/perl
while( <DATA> ) {
    chomp;
    print "BEFORE: $_\n";
    s/(.)(\1)+//g;
    print "AFTER: $_\n";
    print "charater: " . substr($_,0,1) . "\n\n";
 }
__END__
aabbccddeef
faabbccddeef
faabbccddeefax
xfaabbccddeefx
xabcdefghai
cccdddeeea12345
1234a5678a23
aabbcdecc
abcdefg
aabbccddeef
cccdddeeea12345

在“给出第一个字符”的简单情况下,这个方法是有效的。(编辑:重新阅读:抱歉,我现在才读到明显的删除重复不是你要找的...)

如果有更好的解决方案,我很乐意听取建议。


这个正则表达式:m/(?:(.)\1+)*(.?)/ 确实有效!即使是行首和组内的单例也在 \2 中。不错的谜题... - dawg

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