在string中查找“出现两次的单个字母”

我试图捕捉如果一个字母使用正则expression式出现两次(或者有更好的方法?),例如我的string是:

ugknbfddgicrmopn 

输出将是:

 dd 

不过,我试过类似的东西:

 re.findall('[az]{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'] 

您需要使用基于捕获组的正则expression式,并将您的正则expression式定义为原始string。

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

要么

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

注意,这里的re.findall应该返回第一个元素和第二个元素匹配的字符的元组列表。 对于我们的第一组中的字符就足够了,所以我提到了i[0]

作为Pythonic的一种方式你可以在列表理解中使用zip函数:

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

如果处理大string,可以使用iter()函数将string转换为迭代器,并使用itertols.tee()创build两个独立的迭代器,然后通过调用第二个迭代器的next函数消耗第一个项目并使用call 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'(([az])\2)', 'abbbbcppq')]" 100000 loops, best of 3: 3.21 usec per loop 

在注释中提到的最后一次编辑之后,如果只想匹配"abbbcppq"string中的一对b ,则可以使用finditer()返回匹配对象的迭代器,并使用group()方法提取结果:

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

请注意, re.I是使RegEx与大写字母匹配的IGNORECASE标志。

使用反向引用,很简单:

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

有关更多详细信息,可以参考perl中的类似问题: 正则expression式匹配任何重复超过10次的字符

也许你可以使用发生器来实现这一点

 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'] 

没有正则expression式是很容易的:

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

“或者有更好的办法”

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

下面的伪代码如何?

 function findMultipleLetters(inputString) { foreach (letter in inputString) { dictionaryOfLettersOccurrance[letter]++; if (dictionaryOfLettersOccurrance[letter] == 2) { multipleLetters.add(letter); } } return multipleLetters; } multipleLetters = findMultipleLetters("ugknbfddgicrmopn"); 
 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 
 >>> l = ['ug', 'kn', 'bf', 'dd', 'gi', 'cr', 'mo', 'pn'] >>> import re >>> newList = [item for item in l if re.search(r"([az]{1})\1", item)] >>> newList ['dd']