Expressões regulares Python são uma linguagem poderosa para combinar padrões de texto. Esta página dá uma introdução básica às próprias expressões regulares suficientes para os nossos exercícios Python e mostra como as expressões regulares funcionam em Python. O módulo “re” do Python fornece suporte a expressões regulares.

Em Python uma busca de expressão regular é tipicamente escrita como:

 match = re.search(pat, str)

O método re.search() pega um padrão de expressão regular e uma string e busca por esse padrão dentro da string. Se a busca for bem sucedida, search() retorna um objeto correspondente ou None caso contrário. Portanto, a busca é geralmente imediatamente seguida por um if-statement para testar se a busca foi bem sucedida, como mostrado no seguinte exemplo que busca o padrão ‘word:’ seguido por uma palavra de 3 letras (detalhes abaixo):

str = 'an example word:cat!!'match = re.search(r'word:\w\w\w', str)# If-statement after search() tests if it succeededif match: print 'found', match.group() ## 'found word:cat'else: print 'did not find'

O código match = re.search(pat, str) armazena o resultado da busca em uma variável chamada “match”. Então o if-statement testa a correspondência — se verdadeiro a busca foi bem sucedida e match.group() é o texto correspondente (por exemplo, ‘word:cat’). Caso contrário, se a correspondência for falsa (Nenhuma para ser mais específica), então a busca não teve sucesso, e não há texto correspondente.

O ‘r’ no início da string padrão designa uma string python “bruta” que passa por barras invertidas sem alteração, o que é muito útil para expressões regulares (Java precisa muito desta funcionalidade!). Eu recomendo que você sempre escreva strings padrão com o ‘r’ apenas como um hábito.

Basic Patterns

O poder das expressões regulares é que elas podem especificar padrões, não apenas caracteres fixos. Aqui estão os padrões mais básicos que combinam com caracteres simples:

  • a, X, 9, < — caracteres comuns combinam exatamente com eles mesmos. Os meta-caracteres que não combinam com eles mesmos porque têm significados especiais são: . ^ $ * + ? { \ | ( ) (detalhes abaixo)
  • . (um ponto) — corresponde a qualquer caractere, exceto a nova linha ‘\n’
  • \w — (minúsculas w) corresponde a um caractere de “palavra”: uma letra ou dígito ou barra inferior . Note que embora “palavra” seja a mnemônica para isso, ela só corresponde a uma única palavra char, não a uma palavra inteira. \W (maiúscula W) corresponde a qualquer caractere que não seja uma palavra.
  • \b — limite entre palavra e não palavra
  • \s — (minúsculas s) corresponde a um único caractere de espaço em branco — espaço, nova linha, retorno, separador, forma . \S (maiúsculas S) corresponde a qualquer caractere que não seja do espaço em branco.
  • \t, \n, \r — tab, newline, return
  • \d — dígito decimal (alguns utilitários regex mais antigos não suportam, mas todos eles suportam w e \s)
  • ^ = início, $ = fim — corresponde ao início ou fim da string
  • ^ — inibe a “especialidade” de um caractere. Então, por exemplo, use \. para combinar um ponto ou para combinar uma barra. Se você não tem certeza se um caractere tem um significado especial, como ‘@’, você pode colocar uma barra na frente dele, \@, para ter certeza que ele é tratado como um caractere.

Basic Examples

Joke: como você chama um porco com três olhos? piiig!

As regras básicas da procura de expressão regular de um padrão dentro de uma corda são:

  • A procura prossegue através da corda do início ao fim, parando na primeira correspondência encontrada
  • Todos os padrões devem ser correspondidos, mas não toda a corda
  • Se match = re.search(pat, str) for bem sucedida, a correspondência não é Nenhuma e em particular a correspondência.group() é o texto de correspondência
 ## Search for pattern 'iii' in string 'piiig'. ## All of the pattern must match, but it may appear anywhere. ## On success, match.group() is matched text. match = re.search(r'iii', 'piiig') # found, match.group() == "iii" match = re.search(r'igs', 'piiig') # not found, match == None ## . = any char but \n match = re.search(r'..g', 'piiig') # found, match.group() == "iig" ## \d = digit char, \w = word char match = re.search(r'\d\d\d', 'p123g') # found, match.group() == "123" match = re.search(r'\w\w\w', '@@abcd!!') # found, match.group() == "abc"

Repetição

As coisas ficam mais interessantes quando você usa + e * para especificar repetição no padrão

  • + — 1 ou mais ocorrências do padrão à sua esquerda, por exemplo ‘i+’ = um ou mais i’s
  • * — 0 ou mais ocorrências do padrão à sua esquerda
  • ? — corresponde a 0 ou 1 ocorrências do padrão à sua esquerda

Leftmost & Largest

Primeiro a pesquisa encontra a maior correspondência à esquerda para o padrão, e segundo tenta usar o máximo possível da string — isto é + e * vão o mais longe possível (os + e * são ditos “gananciosos”).

Exemplos de repetição

 ## i+ = one or more i's, as many as possible. match = re.search(r'pi+', 'piiig') # found, match.group() == "piii" ## Finds the first/leftmost solution, and within it drives the + ## as far as possible (aka 'leftmost and largest'). ## In this example, note that it does not get to the second set of i's. match = re.search(r'i+', 'piigiiii') # found, match.group() == "ii" ## \s* = zero or more whitespace chars ## Here look for 3 digits, possibly separated by whitespace. match = re.search(r'\d\s*\d\s*\d', 'xx1 2 3xx') # found, match.group() == "1 2 3" match = re.search(r'\d\s*\d\s*\d', 'xx12 3xx') # found, match.group() == "12 3" match = re.search(r'\d\s*\d\s*\d', 'xx123xx') # found, match.group() == "123" ## ^ = matches the start of string, so this fails: match = re.search(r'^b\w+', 'foobar') # not found, match == None ## but without the ^ it succeeds: match = re.search(r'b\w+', 'foobar') # found, match.group() == "bar"

Exemplo de e-mail

Se quiser encontrar o endereço de e-mail dentro da string ‘xyz [email protected] purple monkey’. Vamos usar isto como um exemplo em execução para demonstrar características de expressão mais regulares. Aqui está uma tentativa usando o padrão r’\w+@\w+’:

 str = 'purple [email protected] monkey dishwasher' match = re.search(r'\w+@\w+', str) if match: print match.group() ## 'b@google'

A busca não obtém o endereço de e-mail completo neste caso porque o \w não corresponde ao ‘-‘ ou ‘.’ no endereço. Vamos corrigir isso usando os recursos de expressão regular abaixo.

Parênteses Quadrados

Parênteses Quadrados podem ser usados para indicar um conjunto de caracteres, então combina ‘a’ ou ‘b’ ou ‘c’. Os códigos \w, \s, etc. também funcionam dentro de parênteses rectos com a única excepção que o ponto (.) significa apenas um ponto literal. Para o problema dos emails, os colchetes são uma maneira fácil de adicionar ‘.’ e ‘-‘ ao conjunto de caracteres que podem aparecer em torno do @ com o padrão r’+@+’ para obter o endereço de email completo:

 match = re.search(r'+@+', str) if match: print match.group() ## '[email protected]'

(Mais características de colchetes) Você também pode usar um traço para indicar um intervalo, então combina com todas as letras minúsculas. Para usar um traço sem indicar um intervalo, coloque o traço em último lugar, por exemplo . Um up-hat (^) no início de um conjunto de travessão quadrado o inverte, então significa qualquer gráfico exceto ‘a’ ou ‘b’.

Extração de grupo

A característica “grupo” de uma expressão regular permite que você escolha partes do texto correspondente. Suponha para o problema de e-mails que queremos extrair o nome de usuário e hospedar separadamente. Para fazer isso, adicione parênteses ( ) ao redor do nome de usuário e do host no padrão, assim: r'(+)@(+)’. Neste caso, os parênteses não mudam o que o padrão irá combinar, mas sim estabelecem “grupos” lógicos dentro do texto da combinação. Em uma pesquisa bem sucedida, match.group(1) é o texto correspondente ao 1º parêntese esquerdo, e match.group(2) é o texto correspondente ao 2º parêntese esquerdo. O simples match.group() ainda é todo o texto correspondente como de costume.

 str = 'purple [email protected] monkey dishwasher' match = re.search(r'(+)@(+)', str) if match: print match.group() ## '[email protected]' (the whole match) print match.group(1) ## 'alice-b' (the username, group 1) print match.group(2) ## 'google.com' (the host, group 2)

Um fluxo de trabalho comum com expressões regulares é que você escreve um padrão para o que você está procurando, adicionando grupos de parênteses para extrair as partes que você quer.

findall

findall() é provavelmente a função mais poderosa no módulo re. Acima usamos re.search() para encontrar a primeira correspondência para um padrão. findall() encontra *todas* as correspondências e as retorna como uma lista de strings, com cada string representando uma correspondência.

 ## Suppose we have a text with many email addresses str = 'purple [email protected], blah monkey [email protected] blah dishwasher' ## Here re.findall() returns a list of all the found email strings emails = re.findall(r'+@+', str) ## for email in emails: # do something with each found email string print email

findall With Files

Para arquivos, você pode ter o hábito de escrever um loop para iterar sobre as linhas do arquivo, e você poderia então chamar findall() em cada linha. Ao invés disso, deixe findall() fazer a iteração para você — muito melhor! Apenas alimente todo o texto do arquivo em findall() e deixe-o retornar uma lista de todas as correspondências em um único passo (lembre-se que f.read() retorna todo o texto de um arquivo em uma única string):

 # Open file f = open('test.txt', 'r') # Feed the file text into findall(); it returns a list of all the found strings strings = re.findall(r'some pattern', f.read())

findall e Grupos

O mecanismo de grupo entre parênteses ( ) pode ser combinado com findall(). Se o padrão inclui 2 ou mais grupos de parênteses, então ao invés de retornar uma lista de strings, findall() retorna uma lista de *tuplos*. Cada tuple representa uma correspondência do padrão, e dentro do tuple está o grupo(1), grupo(2) … dados. Então se 2 grupos entre parênteses são adicionados ao padrão de email, então findall() retorna uma lista de tuples, cada comprimento 2 contendo o nome de usuário e o host, por exemplo (‘alice’, ‘google.com’).

 str = 'purple [email protected], blah monkey [email protected] blah dishwasher' tuples = re.findall(r'(+)@(+)', str) print tuples ## for tuple in tuples: print tuple ## username print tuple ## host

Após você ter a lista de tuples, você pode fazer um loop sobre ela para fazer alguns cálculos para cada tuple. Se o padrão não inclui parênteses, então findall() retorna uma lista de strings encontradas, como nos exemplos anteriores. Se o padrão inclui um único conjunto de parênteses, então findall() retorna uma lista de cadeias de caracteres correspondentes a esse único grupo. (Obscuro recurso opcional: Às vezes você tem agrupamentos de parênteses ( ) no padrão, mas que você não quer extrair. Nesse caso, escreva os parênteses com um ?: no início, por exemplo (?: ) e esse paren esquerdo não contará como resultado do grupo.)

RE Workflow e Debug

Regular expression patterns pack a lot of meaning into just few characters , mas eles são tão densos, que você pode gastar muito tempo depurando seus padrões. Configure seu tempo de execução para que você possa executar um padrão e imprimir o que ele combina facilmente, por exemplo, executando-o em um pequeno texto de teste e imprimindo o resultado de findall(). Se o padrão não corresponder a nada, tente enfraquecer o padrão, removendo partes dele para que você obtenha muitas correspondências. Quando ele não corresponde a nada, você não pode fazer nenhum progresso, já que não há nada concreto para se olhar. Uma vez que ele está combinando demais, então você pode trabalhar em apertá-lo incrementalmente para atingir apenas o que você quer.

Options

As funções re tomam opções para modificar o comportamento do pattern match. A opção flag é adicionada como um argumento extra à busca() ou findall() etc., por exemplo, re.search(pat, str, re.IGNORECASE).

  • IGNORECASE — ignore as diferenças entre maiúsculas e minúsculas para a correspondência, então ‘a’ corresponde tanto a ‘a’ como a ‘A’.
  • DOTALL — permite que o ponto (.) combine com a nova linha — normalmente combina com qualquer coisa menos com a nova linha. Isso pode fazer você tropeçar — você acha que .* combina tudo, mas por padrão não passa do fim de uma linha. Note que \s (espaço em branco) inclui novas linhas, então se você quiser combinar um espaço em branco que pode incluir uma nova linha, você pode apenas usar \s*
  • MULTILINE — Dentro de uma string feita de muitas linhas, permita que ^ e $ combinem o início e o fim de cada linha. Normalmente ^/$ apenas combinariam o início e fim de toda a string.

Greedy vs. Non-Greedy (opcional)

Esta é uma seção opcional que mostra uma técnica de expressão regular mais avançada não necessária para os exercícios.

Suponha que você tenha texto com tags nele: <b>foo</b> e <i>sobre</i>

Suponha que você está tentando combinar cada tag com o padrão ‘(<.*>)’ — o que combina primeiro?

O resultado é um pouco surpreendente, mas o aspecto ganancioso do .* faz com que ele combine com o conjunto ‘<b>foo</b> e <i>sobre</i>’ como uma grande combinação. O problema é que o .* vai o mais longe possível, em vez de parar no primeiro > (também conhecido como “ganancioso”).

Existe uma extensão para expressão regular onde se adiciona um ? no final, tal como .*? ou .+?, mudando-os para não serem gananciosos. Agora eles param assim que podem. Então o padrão ‘(<.*?>)’ receberá apenas ‘<b>’ como primeira partida, e ‘</b>’ como segunda partida, e assim sucessivamente obtendo cada <…> par por sua vez. O estilo é tipicamente que você usa um .*?, e então imediatamente seu olhar direito para algum marcador concreto (> neste caso) que força o fim da execução .*? run.

A extensão *? originada em Perl, e expressões regulares que incluem as extensões do Perl são conhecidas como Perl Compatible Regular Expressions — pcre. Python inclui suporte a pcre. Muitos utils de linha de comando, etc. têm uma bandeira onde aceitam padrões pcre.

Uma técnica mais antiga mas amplamente usada para codificar esta idéia de “todos estes caracteres, exceto parar no X” usa o estilo quadrático. Para o acima você poderia escrever o padrão, mas ao invés de .* para obter todos os gráficos, use * que salta sobre todos os caracteres que não são > (o principal ^ “inverte” o conjunto de colchetes, então ele combina com qualquer gráfico não entre parênteses).

Substitution (opcional)

A função re.sub(pat, replacement, str) procura por todas as instâncias de padrão na string dada, e as substitui. A string de substituição pode incluir ‘\1’, ‘\2’ que se referem ao texto do grupo(1), grupo(2), e assim por diante a partir do texto original correspondente.

Aqui está um exemplo que procura todos os endereços de e-mail, e os altera para manter o usuário (\1) mas tem yo-yo-dyne.com como hospedeiro.

 str = 'purple [email protected], blah monkey [email protected] blah dishwasher' ## re.sub(pat, replacement, str) -- returns new string with all replacements, ##  is group(1),  group(2) in the replacement print re.sub(r'(+)@(+)', r'@yo-yo-dyne.com', str) ## purple [email protected], blah monkey [email protected] blah dishwasher

Exercício

Para praticar expressões regulares, veja o Exercício de Nomes de Bebês.

admin

Deixe uma resposta

O seu endereço de email não será publicado.

lg