Você já conhece o básico: classes de caracteres como \d e [A-Z],
quantificadores como {4} e +, e as palavras matches?,
all-matching-subseqs e re-replace. Este exercício acrescenta as partes
das expressões regulares que descrevem estrutura e contexto. Elas
vivem no mesmo vocabulário regexp, escritas com o mesmo
literal R/ /.
As âncoras correspondem a uma posição, e não a um caractere:
^ ! the start of the string
$ ! the end of the string
\b ! a word boundary
Como $ significa o fim da string, escreva \$ quando quiser dizer um
cifrão literal.
Uma fronteira de palavra (\b) é o ponto onde um caractere de palavra
encontra um que não é de palavra. Os caracteres de palavra são letras,
dígitos e o sublinhado; todo o resto (espaços, pontuação e as duas pontas
da string) conta como fronteira. Então \bcat\b corresponde a cat apenas
quando ele aparece sozinho:
USING: regexp ;
"cat nap" R/ \bcat\b/ re-contains? . ! => t
"scatter" R/ \bcat\b/ re-contains? . ! => f (cat sits inside a word)
Os parênteses redondos ( ) agrupam parte de um padrão para que um
quantificador, ou um | ("isto ou aquilo"), se aplique ao grupo
inteiro:
USING: regexp ;
"abab" R/ (ab)+/ matches? . ! => t (+ applies to "ab")
"dog" R/ cat|dog/ matches? . ! => t (either word)
"$100.50" R/ \$\d+(\.\d{2})?/ matches? . ! => t
"$100" R/ \$\d+(\.\d{2})?/ matches? . ! => t (the group is optional)
O último padrão se lê assim: "um cifrão, um ou mais dígitos e, em seguida, um grupo opcional formado por um ponto e dois dígitos".
Nota: no Factor, um grupo apenas estrutura o padrão. Diferente de algumas ferramentas de regex, você não consegue recuperar o texto com que um grupo correspondeu: não há extração de grupos de captura, e referências retroativas como
\1não são suportadas. Para extrair texto em volta de um ponto de referência, use o lookaround (veja abaixo).
Um lookaround verifica o que vem logo antes ou logo depois do ponto atual sem tornar esses caracteres parte da correspondência. Ele tem "largura zero": o que ele testa não entra no resultado.
(?=...) ! lookahead: ... must follow
(?!...) ! negative lookahead: ... must NOT follow
(?<=...) ! lookbehind: ... must come before
(?<!...) ! negative lookbehind: ... must NOT come before
O lookaround é seletivo: os mesmos caracteres correspondem ou não,
dependendo do que está ao lado deles. Um \d+ simples pega todos os
números; o lookbehind e o lookahead mantêm apenas os que têm o vizinho
certo:
USING: regexp ;
"3 for $10 or 10 for $30" R/ \d+/ all-matching-subseqs .
! => { "3" "10" "10" "30" } (every number)
"3 for $10 or 10 for $30" R/ (?<=\$)\d+/ all-matching-subseqs .
! => { "10" "30" } (only the prices — the bare 3 and 10 drop)
"buy 2 at 15% off, 4 at 30% off" R/ \d+(?=%)/ all-matching-subseqs .
! => { "15" "30" } (only the discounts — the bare 2 and 4 drop)
Adicione uma letra depois da / de fechamento para mudar o comportamento
do padrão inteiro. O i o torna insensível a maiúsculas e minúsculas:
USING: regexp ;
"REFUND" R/ refund/i matches? . ! => t
"Refund" R/ refund/i matches? . ! => t
all-matching-subseqs entrega o texto da correspondência. O map-matches
vai um passo além: ele roda uma quotation em cada correspondência e
junta os resultados. A quotation recebe o índice start da
correspondência, o índice end dela e a string inteira; o subseq
transforma esses três no texto da correspondência, que você então
transforma:
map-matches ( string regexp quot: ( start end string -- obj ) -- seq )
USING: math regexp sequences ;
"a1 b22 c333" R/ \d+/ [ subseq length ] map-matches .
! => { 1 2 3 } (the length of each run of digits)
Seu primo que só produz efeitos colaterais, each-match,
roda uma quotation em cada correspondência sem juntar nada.
Você cuida da contabilidade de uma pequena empresa, e as transações de cada dia chegam como linhas de texto desorganizadas. Você vai usar expressões regulares para validar valores, extrair números pelo símbolo que os marca e sinalizar as linhas que precisam de uma olhada mais de perto.
Um valor bem formado é um $, um ou mais dígitos e uma parte de centavos
opcional, formada por um ponto seguido de exatamente dois dígitos:
$100 ou $100.50, mas não $100.5. Defina valid-amount?, que
retorna t quando a string inteira for um valor bem formado.
"$100.50" valid-amount? . ! => t
"$100" valid-amount? . ! => t
"$100.5" valid-amount? . ! => f
Defina dollar-amounts, que retorna todo número que vem logo depois de
um $ na linha (apenas os dígitos, sem o $), na ordem.
"spent $100 and $25 today" dollar-amounts .
! => { "100" "25" }
Defina percentages, que retorna todo número seguido imediatamente por
um %.
"up 5% then down 12%" percentages .
! => { "5" "12" }
Defina flagged?, que retorna t quando a linha menciona refund ou
chargeback em qualquer combinação de maiúsculas e minúsculas.
"Issued a REFUND today" flagged? . ! => t
"a normal sale" flagged? . ! => f
Crie sua conta no Exercism para aprender e dominar Factor com 47 conceitos163 exercícios e mentoria humana de verdade, tudo de graça.