web-dev-qa-db-fra.com

Rechercher "une lettre qui apparaît deux fois" dans une chaîne

J'essaie d'attraper si une lettre qui apparaît deux fois dans une chaîne en utilisant RegEx (ou peut-être qu'il existe de meilleures façons?), Par exemple ma chaîne est:

ugknbfddgicrmopn

La sortie serait:

dd

Cependant, j'ai essayé quelque chose comme:

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

mais dans ce cas, il renvoie:

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

J'ai également un moyen d'obtenir la sortie attendue:

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

Mais c'est trop complexe ...


Si c'est 'abbbcppq', alors attraper seulement:

abbbcppq
 ^^  ^^

La sortie est donc:

['bb', 'pp']

Ensuite, si c'est 'abbbbcppq', attraper bb deux fois:

abbbbcppq
 ^^^^ ^^

La sortie est donc:

['bb', 'bb', 'pp']
56
Kevin Guan

Vous devez utiliser la capture d'expression régulière basée sur un groupe et définir votre expression régulière en tant que chaîne brute.

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

ou

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

Notez que , re.findall ici devrait renvoyer la liste des tuples avec les caractères qui sont mis en correspondance par le premier groupe comme premier élément et le deuxième groupe comme deuxième élément. Pour nos cas, les caractères au sein du premier groupe seraient suffisants, j'ai donc mentionné i[0].

50
Avinash Raj

De manière Pythonique Vous pouvez utiliser la fonction Zip dans une liste de compréhension:

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

Si vous avez affaire à une chaîne de grande taille, vous pouvez utiliser la fonction iter() pour convertir la chaîne en itérateur et utiliser itertols.tee() pour créer deux itérateurs indépendants, puis en appelant next la fonction sur le deuxième itérateur consomme le premier élément et utilise l'appel à la classe Zip (dans Python 2.X utilisez itertools.izip() qui retourne un itérateur) avec cet itérateur) .

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

Référence avec RegEx recette:

# 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

Après votre dernière modification, comme indiqué dans le commentaire, si vous ne souhaitez faire correspondre qu’une paire de b dans des chaînes comme "abbbcppq", Vous pouvez utiliser finditer() qui renvoie un itérateur d’objets correspondants, et extrayez le résultat avec la méthode group():

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

Notez que re.I Est l'indicateur IGNORECASE qui fait que le RegEx correspond également aux lettres majuscules.

32
Kasrâmvd

En utilisant la référence arrière, c'est très simple:

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

Pour plus de détails, vous pouvez vous référer à une question similaire en Perl: Expression régulière pour correspondre à n'importe quel caractère répété plus de 10 fois

9
Gurupad Hegde

C'est assez facile sans expressions régulières:

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

Vous pouvez peut-être utiliser le générateur pour y parvenir

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']
4
xhg
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
2
Mark White

"ou peut-être qu'il y a de meilleures façons"

Étant donné que l'expression régulière est souvent mal comprise par le prochain développeur pour rencontrer votre code (peut-être même vous), et puisque plus simple! = Plus court,

Que diriez-vous du pseudo-code suivant:

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