Encontrando um padrão, não uma string exata

Prévia grátis 3 min de leitura

"Isso parece um email?" é outra pergunta que "isso é aquela string?"

input = "[email protected]"
input == "[email protected]"   # true, mas só para esse endereço exato

Essa comparação responde a uma pergunta sobre uma string específica. Um formulário de cadastro não sabe de antemão o email de quem visita. Ele precisa checar "isso parece um email": uma forma, e não um valor exato. O == não consegue dizer "alguns caracteres, depois um @, depois mais caracteres, depois um ponto, depois algumas letras". Uma expressão regular (regex, para encurtar) consegue.

Um literal de regex, escrito em Ruby

pattern = /gmail/
pattern.class
# Regexp

/gmail/ é um objeto Regexp, um objeto Ruby como qualquer outro, criado com barras em vez de Regexp.new. O regex mais simples é texto puro. /gmail/ casa com qualquer string que tenha os caracteres g, m, a, i, l nessa ordem, em qualquer ponto dela.

=~: o primeiro jeito de rodar um

"[email protected]" =~ /gmail/
# 4, o índice onde o match começa

"[email protected]" =~ /gmail/
# nil, nenhum match em lugar nenhum da string

O =~ roda o regex contra uma string e devolve o índice onde começa o primeiro match, ou nil se nada casar. nil é falsy e todo inteiro (inclusive 0) é truthy, então o =~ cabe direto num if:

email = "[email protected]"
if email =~ /gmail/
  puts "parece um endereço do Gmail"
end
# "parece um endereço do Gmail"

Cuidado com uma ponta afiada. Um match no índice 0 continua sendo match, e 0 é truthy em Ruby (só nil e false são falsy), então esse padrão é seguro. Ele quebraria com um método que devolvesse false quando não há match, em vez de nil. Por isso o contrato "índice ou nil" do =~ importa.

Nem todo caractere significa ele mesmo

., *, +, ?, [, ], (, ), {, }, |, \, ^ e $ têm significados especiais dentro de um regex. Este curso trata de vários deles a partir da aula 5. Por enquanto: para casar um desses caracteres como texto comum, escape-o com uma barra invertida:

"3.14" =~ /3.14/    # 0, mas "." aqui significa "qualquer caractere", então isso também dá match com "3x14"
"3x14" =~ /3.14/    # 0, deu match mesmo assim, provavelmente não era a intenção
"3.14" =~ /3\.14/  # 0, "\." significa um ponto literal, e só um ponto literal
"3x14" =~ /3\.14/  # nil, corretamente rejeitado

Para onde isso vai

Este curso monta o kit por trás daquela checagem de email. Ele cobre como tirar pedaços estruturados de um match, como achar toda ocorrência em vez da primeira e as âncoras e classes de caracteres que impedem um padrão de ficar frouxo demais. Depois vem busca e substituição. A última aula usa tudo isso para ler uma expressão regular que roda nos models desta app.

Pontos-chave

  • == checa uma string exata; um regex checa uma forma que muitas strings diferentes podem ter.
  • /pattern/ é um objeto Regexp; o =~ o roda contra uma string e devolve o índice do primeiro match, ou nil se não houver match.
  • 0 é um índice de match válido e truthy em Ruby, então if str =~ /pattern/ trata "casou no início" como match.
  • Caracteres como ., *, + e ? são especiais dentro de um regex; para casar um deles como texto comum, escape: \. em vez de ..
Aula 1 de 7 · grátis

Continue com a aula 2

MatchData ou um simples Sim/Não: match vs match?

Compara o match, que devolve MatchData, com o match?, que devolve um booleano e não mexe em $~.

Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.

Digite para buscar...