在字符串中找到“出现两次的一个字母”

时间:2015-12-14 07:00:09

标签: python regex python-2.7

我试图捕捉一个字母是否使用RegEx在字符串中出现两次(或者可能有更好的方法?),例如我的字符串是:

ugknbfddgicrmopn

输出结果为:

dd

但是,我尝试过类似的事情:

re.findall('[a-z]{2}', 'ugknbfddgicrmopn')

但在这种情况下,它返回:

['ug', 'kn', 'bf', 'dd', 'gi', 'cr', 'mo', 'pn']   # the except output is `['dd']`

我也有办法获得期望输出:

>>> l = []
>>> tmp = None
>>> for i in 'ugknbfddgicrmopn':
...     if tmp != i:
...         tmp = i
...         continue
...     l.append(i*2)
...     
... 
>>> l
['dd']
>>> 

但那太复杂了......

如果是'abbbcppq',那么只抓住:

abbbcppq
 ^^  ^^

所以输出是:

['bb', 'pp']

然后,如果它是'abbbbcppq',请抓住bb两次:

abbbbcppq
 ^^^^ ^^

所以输出是:

['bb', 'bb', 'pp']

8 个答案:

答案 0 :(得分:50)

您需要使用基于捕获组的正则表达式并将正则表达式定义为原始字符串。

>>> re.search(r'([a-z])\1', 'ugknbfddgicrmopn').group()
'dd'
>>> [i+i for i in re.findall(r'([a-z])\1', 'abbbbcppq')]
['bb', 'bb', 'pp']

>>> [i[0] for i in re.findall(r'(([a-z])\2)', 'abbbbcppq')]
['bb', 'bb', 'pp']

请注意,此处re.findall应返回元组列表,其中第一个组匹配的字符为第一个元素,第二个组为第二个元素。对于我们的情况,第一组中的字符就足够了所以我提到了i[0]

答案 1 :(得分:32)

作为Pythonic方式您可以在列表解析中使用zip函数:

>>> s = 'abbbcppq'
>>>
>>> [i+j for i,j in zip(s,s[1:]) if i==j]
['bb', 'bb', 'pp']

如果要处理大字符串,可以使用iter()函数将字符串转换为迭代器,并使用itertols.tee()创建两个独立的迭代器,然后通过调用next函数第二个迭代器使用第一个项,并使用此迭代器调用zip类(在Python 2.X中使用itertools.izip()返回迭代器)。

>>> from itertools import tee
>>> first = iter(s)
>>> second, first = tee(first)
>>> next(second)
'a'
>>> [i+j for i,j in zip(first,second) if i==j]
['bb', 'bb', 'pp']

RegEx食谱的基准:

# ZIP
~ $ python -m timeit --setup "s='abbbcppq'" "[i+j for i,j in zip(s,s[1:]) if i==j]"
1000000 loops, best of 3: 1.56 usec per loop

# REGEX
~ $ python -m timeit --setup "s='abbbcppq';import re" "[i[0] for i in re.findall(r'(([a-z])\2)', 'abbbbcppq')]"
100000 loops, best of 3: 3.21 usec per loop

如果您只想在b这样的字符串中匹配一对"abbbcppq",那么在评论中提到的上一次编辑之后,您可以使用finditer()返回匹配对象的迭代器,并且使用group()方法提取结果:

>>> import re
>>> 
>>> s = "abbbcppq"
>>> [item.group(0) for item in re.finditer(r'([a-z])\1',s,re.I)]
['bb', 'pp']

请注意re.I IGNORECASE 标志,它使RegEx也匹配大写字母。

答案 2 :(得分:9)

使用反向引用,非常简单:

import re
p = re.compile(ur'([a-z])\1{1,}')
re.findall(p, u"ugknbfddgicrmopn")
#output: [u'd']
re.findall(p,"abbbcppq")
#output: ['b', 'p']

有关详情,请参阅perl中的类似问题:Regular expression to match any character being repeated more than 10 times

答案 3 :(得分:5)

没有正则表达式很容易:

In [4]: [k for k, v in collections.Counter("abracadabra").items() if v==2]
Out[4]: ['b', 'r']

答案 4 :(得分:4)

也许您可以使用生成器实现此目标

def adj(s):
    last_c = None
    for c in s:
        if c == last_c:
            yield c * 2
        last_c = c

s = 'ugknbfddgicrmopn'
v = [x for x in adj(s)]
print(v)
# output: ['dd']

答案 5 :(得分:2)

A1 = "abcdededdssffffccfxx"

print A1[1]
for i in range(len(A1)-1):
    if A1[i+1] == A1[i]:
        if not A1[i+1] == A1[i-1]:
            print A1[i] *2

答案 6 :(得分:2)

"或者可能有更好的方法"

由于正则表达式经常被下一个开发人员误解为遇到你的代码(甚至可能是你), 而且因为更简单!=更短,

以下伪代码怎么样:

function findMultipleLetters(inputString) {        
    foreach (letter in inputString) {
        dictionaryOfLettersOccurrance[letter]++;
        if (dictionaryOfLettersOccurrance[letter] == 2) {
            multipleLetters.add(letter);
        }
    }
    return multipleLetters;
}
multipleLetters = findMultipleLetters("ugknbfddgicrmopn");

答案 7 :(得分:0)

>>> l = ['ug', 'kn', 'bf', 'dd', 'gi', 'cr', 'mo', 'pn']
>>> import re
>>> newList = [item for item in l if re.search(r"([a-z]{1})\1", item)]
>>> newList
['dd']