string de retorno com primeira correspondência Regex

91

Quero obter a primeira correspondência de uma regex.

Neste caso, obtive uma lista:

text = 'aa33bbb44'
re.findall('\d+',text)

['33', '44']

Eu poderia extrair o primeiro elemento da lista:

text = 'aa33bbb44'
re.findall('\d+',text)[0]

'33'

Mas isso só funciona se houver pelo menos uma correspondência, caso contrário, receberei um erro:

text = 'aazzzbbb'
re.findall('\d+',text)[0]

IndexError: índice da lista fora do intervalo

Nesse caso, eu poderia definir uma função:

def return_first_match(text):
    try:
        result = re.findall('\d+',text)[0]
    except Exception, IndexError:
        result = ''
    return result

Existe uma maneira de obter esse resultado sem definir uma nova função?

Luis Ramon Ramirez Rodriguez
fonte
Para mim, a resposta aceita não funcionou. Tive que remover o acesso ao índice de array e usar len(re.findAll)==0check em seu lugar.
Vishal

Respostas:

109

Você pode incorporar o ''padrão em sua regex adicionando |$:

>>> re.findall('\d+|$', 'aa33bbb44')[0]
'33'
>>> re.findall('\d+|$', 'aazzzbbb')[0]
''
>>> re.findall('\d+|$', '')[0]
''

Também funciona com re.searchapontado por outros:

>>> re.search('\d+|$', 'aa33bbb44').group()
'33'
>>> re.search('\d+|$', 'aazzzbbb').group()
''
>>> re.search('\d+|$', '').group()
''
Stefan Pochmann
fonte
Ótimo, search / .group tem alguma vantagem sobre findall / [0]?
Luis Ramon Ramirez Rodriguez
6
@LuisRamonRamirezRodriguez Bem, ele pode parar assim que encontrar uma correspondência, não precisa processar o resto do texto e não precisa armazenar todas as correspondências. Portanto, é mais eficiente. Além disso, literalmente "é o que você deseja" , como disse @TimPeters. Isso pode ser uma vantagem quando você ou outra pessoa em algum momento o ler e se perguntar "Por que foi findallusado?" .
Stefan Pochmann
43

Se você só precisa da primeira correspondência, use em re.searchvez de re.findall:

>>> m = re.search('\d+', 'aa33bbb44')
>>> m.group()
'33'
>>> m = re.search('\d+', 'aazzzbbb')
>>> m.group()
Traceback (most recent call last):
  File "<pyshell#281>", line 1, in <module>
    m.group()
AttributeError: 'NoneType' object has no attribute 'group'

Em seguida, você pode usar mcomo uma condição de verificação como:

>>> m = re.search('\d+', 'aa33bbb44')
>>> if m:
        print('First number found = {}'.format(m.group()))
    else:
        print('Not Found')


First number found = 33
Punho de ferro
fonte
13

Eu iria com:

r = re.search("\d+", ch)
result = return r.group(0) if r else ""

re.searchsó procura a primeira correspondência na string de qualquer maneira, então acho que torna sua intenção um pouco mais clara do que usar findall.

Conta
fonte
9

Você não deveria estar usando .findall()- .search()é o que você quer. Ele encontra a correspondência mais à esquerda, que é o que você deseja (ou retorna Nonese não houver correspondência).

m = re.search(pattern, text)
result = m.group(0) if m else ""

Se você deseja colocar isso em uma função, depende de você. É incomum querer retornar uma string vazia se nenhuma correspondência for encontrada, e é por isso que nada parecido com isso está embutido. É impossível se confundir sobre se .search()por si só encontra uma correspondência (retorna Nonese não encontrou, ou um SRE_Matchobjeto se assim fosse).

Tim Peters
fonte
3

Você pode fazer:

x = re.findall('\d+', text)
result = x[0] if len(x) > 0 else ''

Observe que sua pergunta não está exatamente relacionada ao regex. Em vez disso, como você encontra com segurança um elemento de uma matriz, se não houver nenhum.

cetan vijayvargiya
fonte
2
Eu substituiria 'len (x)> 0' simplesmente por 'x' aqui.
Ulf Aslak
1

Talvez isso tivesse um desempenho um pouco melhor no caso de uma quantidade maior de dados de entrada não conter sua peça desejada porque exceto tem um custo maior.

def return_first_match(text):
    result = re.findall('\d+',text)
    result = result[0] if result else ""
    return result
Marko Mackic
fonte