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