Regexes não gananciosas do Python

150

Como faço para criar uma regex python como "(.*)"essa, dada a "a (b) c (d) e"correspondência python em "b"vez de "b) c (d"?

Eu sei que posso usar em "[^)]"vez disso ".", mas estou procurando uma solução mais geral que mantenha meu regex um pouco mais limpo. Existe alguma maneira de dizer ao python "ei, combine isso o mais rápido possível"?

So8res
fonte

Respostas:

209

Você procura o todo-poderoso *?

Dos documentos, ganancioso versus não ganancioso

os qualificadores não-gananciosos *?, +?, ??, ou {m,n}?[...] partida como pouco texto possível.

Trey Stout
fonte
De acordo com o Internet Archive, todo esse link apontado era uma cópia dos documentos do módulo "re" do Python; portanto, o link do Trey também funciona.
Spiffytech 13/07/12
2
qual é o nome em inglês comum para isso *??
Trevor Boyd Smith
Caracteres curinga @Trevor Boyd Smith
Serge
3
Isso é chamado de "non gananciosos" qualificador
brunetton
65
>>> x = "a (b) c (d) e"
>>> re.search(r"\(.*\)", x).group()
'(b) c (d)'
>>> re.search(r"\(.*?\)", x).group()
'(b)'

De acordo com os documentos :

Os qualificadores ' *', ' +' e ' ?' são todos gananciosos; eles correspondem ao máximo de texto possível. Às vezes, esse comportamento não é desejado; se o ER <.*>for comparado com ' <H1>title</H1>', ele corresponderá a toda a cadeia, e não apenas ' <H1>'. Adicionar ' ?' após o qualificador faz com que ele execute a partida de maneira não gananciosa ou mínima; o menor número possível de caracteres será correspondido. Usar .*?na expressão anterior corresponderá apenas ' <H1>'.

Paolo Bergantino
fonte
14

Não \\(.*?\\)funcionaria? Essa é a sintaxe não gananciosa.

Zitrax
fonte
5

Como os outros disseram usando o? O modificador no quantificador * resolverá seu problema imediato, mas tenha cuidado, você está começando a se desviar para áreas onde as expressões regulares param de funcionar e você precisa de um analisador. Por exemplo, a string "(foo (bar)) baz" causará problemas.

Chas. Owens
fonte
5

Usar uma partida desagradável é um bom começo, mas eu também sugiro que você reconsidere qualquer uso de .*- e quanto a isso?

groups = re.search(r"\([^)]*\)", x)
ojrac
fonte
3

Deseja que ele corresponda a "(b)"? Faça como Zitrax e Paolo sugeriram. Deseja que ele corresponda a "b"? Faz

>>> x = "a (b) c (d) e"
>>> re.search(r"\((.*?)\)", x).group(1)
'b'
David Berger
fonte
0

Para começar, não sugiro usar "*" nas expressões regulares. Sim, eu sei, é o delimitador de vários caracteres mais usado, mas, no entanto, é uma má ideia. Isso ocorre porque, embora corresponda a qualquer quantidade de repetição para esse caractere, "any" inclui 0, que geralmente é algo para o qual você deseja gerar um erro de sintaxe, não aceita. Em vez disso, sugiro usar o +sinal, que corresponde a qualquer repetição de comprimento> 1. Além disso, pelo que posso ver, você está lidando com expressões entre parênteses de comprimento fixo. Como resultado, você provavelmente pode usar a {x, y}sintaxe para especificar especificamente o comprimento desejado.

No entanto, se você realmente precisa de repetições não gananciosas, sugiro consultar o todo-poderoso ? . Isso, quando colocado após o final de qualquer especificador de repetição de regex, forçará essa parte do regex a encontrar a menor quantidade possível de texto.

Dito isto, eu teria muito cuidado com o ?que ele tem, como a chave de fenda sônica no Dr. Who, como tem que fazer, como devo dizer, coisas "levemente" indesejadas se não forem calibradas com cuidado. Por exemplo, para usar sua entrada de exemplo, ela identificaria ((1)(observe a falta de um segundo par) como uma correspondência.

The Daleks
fonte