Trilhas
/
jq
jq
/
Exercícios
/
Chatbot regular
Chatbot regular

Chatbot regular

Exercício de aprendizagem

Introdução

Expressões regulares

Expressões regulares (regexes) são sequências de caracteres que especificam um padrão de busca em um texto.

Aprender a sintaxe de expressões regulares está além do escopo deste tópico. Vamos nos concentrar nas expressões que o jq oferece para usar regexes.

Variante de regex

Ferramentas diferentes implementam versões diferentes de expressões regulares. O jq incorpora a biblioteca de regex Oniguruma, que é amplamente compatível com as regexes do Perl v5.8.

A sintaxe específica usada pelo jq pode ser encontrada no repositório do Oniguruma no GitHub.

Caution

O jq não tem nenhuma sintaxe especial para expressões regulares. Elas são simplesmente expressas como strings. Isso significa que qualquer barra invertida na expressão regular precisa ser escapada na string.

Por exemplo, a classe de caractere de dígito (\d) deve ser escrita como "\\d".

Funções de regex

As expressões regulares no jq se limitam a um conjunto de filtros.

Correspondência simples

Quando você precisa saber se uma string corresponde a um padrão, use o filtro test.

STRING | test(REGEX)
STRING | test(REGEX; FLAGS)
STRING | test([REGEX, FLAGS])

Esse filtro gera um resultado boolean.

"Hello World!" | test("W")    # => true
"Goodbye Mars" | test("W")    # => false
Informações sobre a correspondência

Quando você precisa extrair a substring que de fato correspondeu ao padrão, use o filtro match.

STRING | match(REGEX)
STRING | match(REGEX; FLAGS)
STRING | match([REGEX, FLAGS])

Esse filtro gera:

  • nada, se não houve correspondência, ou
  • um objeto contendo várias propriedades, se houve correspondência.

Este exemplo procura duas vogais consecutivas idênticas usando a sintaxe de retro-referência, \1.

"Hello World!" | match("([aeiou])\\1")
# => empty

"Goodbye Mars" | match("([aeiou])\\1")
# => {
#      "offset": 1,
#      "length": 2,
#      "string": "oo",
#      "captures": [
#        {
#          "offset": 1,
#          "length": 1,
#          "string": "o",
#          "name": null
#        }
#      ]
#    }

O filtro match retorna um objeto para cada correspondência. Este exemplo mostra a flag "g" em ação para encontrar todas as vogais.

"Goodbye Mars" | match("[aeiou]"; "g")
# => { "offset": 1, "length": 1, "string": "o", "captures": [] }
#    { "offset": 2, "length": 1, "string": "o", "captures": [] }
#    { "offset": 6, "length": 1, "string": "e", "captures": [] }
#    { "offset": 9, "length": 1, "string": "a", "captures": [] }
Substrings capturadas

Semelhante ao filtro match, o filtro capture retorna um objeto se houve correspondência.

STRING | capture(REGEX)
STRING | capture(REGEX; FLAGS)
STRING | capture([REGEX, FLAGS])

O objeto retornado é um mapeamento das capturas nomeadas.

"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# => {
#      "project": "JIRAISSUE",
#      "issue_num": "1234"
#    }
Apenas as substrings

O filtro scan é semelhante ao match com a flag "g".

STRING | scan(REGEX)
STRING | scan(REGEX; FLAGS)

O scan gera um fluxo de substrings.

"Goodbye Mars" | scan("[aeiou]")
# => "o"
#    "o"
#    "e"
#    "a"

Use o construtor de array [...] para capturar as substrings.

"Goodbye Mars" | [ scan("[aeiou]") ]
# => ["o", "o", "e", "a"]
Dividindo uma string

Se você sabe quais partes da string quer manter, use match ou scan. Se você sabe quais partes quer descartar, use split.

STRING | split(REGEX; FLAGS)
Caution

O filtro split de aridade 1 trata seu argumento como uma string fixa.

Para usar uma regex com split, você precisa fornecer o 2º argumento; não tem problema usar uma string vazia.

Um exemplo que divide uma string em espaços em branco arbitrários.

"first   second           third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
Substituições

Os filtros sub e gsub podem transformar a string de entrada, substituindo as partes correspondentes da entrada por uma string de substituição.

Para substituir apenas a primeira correspondência, use sub. Para substituir todas as correspondências, use gsub.

STRING | sub(REGEX; REPLACEMENT)
STRING | sub(REGEX; REPLACEMENT; FLAGS)
STRING | gsub(REGEX; REPLACEMENT)
STRING | gsub(REGEX; REPLACEMENT; FLAGS)
"Goodnight kittens. Goodnight mittens." | sub("night"; " morning")
# => "Good morning kittens. Goodnight mittens."

"Goodnight kittens. Goodnight mittens." | gsub("night"; " morning")
# => "Good morning kittens. Good morning mittens."

O texto de substituição pode se referir às substrings correspondentes; use capturas nomeadas e interpolação de strings.

"Some 3-letter acronyms: gnu, csv, png"
| gsub( "\\b(?<tla>[[:alpha:]]{3})\\b";     # find words 3 letters long
        "\(.tla | ascii_upcase)" )          # upper-case the match
# => "Some 3-letter acronyms: GNU, CSV, PNG"

Flags

Em todos os filtros acima, FLAGS é uma string composta de zero ou mais das flags compatíveis.

  • g - Busca global (encontra todas as correspondências, não apenas a primeira)
  • i - Busca sem diferenciar maiúsculas e minúsculas
  • m - Modo multilinha ('.' corresponde a quebras de linha)
  • n - Ignora correspondências vazias
  • p - Os modos s e m são ativados
  • s - Modo de linha única ('^' -> '\A', '$' -> '\Z')
  • l - Encontra as correspondências mais longas possíveis
  • x - Formato de regex estendido (ignora espaços em branco e comentários)

Por exemplo

"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")

# or with Extended formatting

"JIRAISSUE-1234" | capture("
                     (?<project>   \\w+ )  # the Jira project
                     -                     # followed by a hyphen
                     (?<issue_num> \\d+ )  # followed by digits
                   "; "x")

Instruções

Contrataram você como Especialista em Expressões Regulares em uma empresa que está desenvolvendo um Chatbot.

Ela está em uma fase muito básica de desenvolvimento. Sua missão é usar Expressões Regulares para melhorar a capacidade do Chatbot de entender e gerar linguagem natural.

1. Verifique se o comando é válido

Além de inteligente, o Chatbot também é um assistente leal. Para perguntar algo ao Chatbot, o usuário deve dizer a palavra "Chatbot" na primeira posição do comando. Não importa se a palavra-chave está em MAIÚSCULAS ou minúsculas. O importante é a posição da palavra.

Implemente a função is_valid_command que ajuda o Chatbot a reconhecer quando o usuário está dando um comando.

"Chatbot, play a song from the 80's." | is_valid_command
# => true
"Hey Chatbot, where is the closest pharmacy?" | is_valid_command
# => false
"CHATBOT, do you have a solution for this challenge?" | is_valid_command
# => true

2. Remova os emojis criptografados

O Chatbot tem dificuldade para entender como as pessoas usam emojis para expressar suas emoções. Quando o Chatbot recebe mensagens de usuários, cada emoji é representado pela string "emoji" seguida de um número de id.

Implemente o método remove_emoji, que recebe uma string e remove todos os emojis da mensagem.

Linhas que não contêm emojis devem ser retornadas sem modificações. Basta remover a string do emoji. Não ajuste os espaços em branco.

"I love playing videogames emoji3465 it's one of my hobbies" | remove_emoji
# => "I love playing videogames  it's one of my hobbies"

3. Verifique se o número de telefone é válido

Em algum momento da interação com o Chatbot, o usuário vai fornecer um número de telefone. O Chatbot só consegue ligar para um número com um formato específico.

Implemente a função check_phone_number.

Se o número for válido, o Chatbot responde com uma mensagem agradecendo ao usuário e confirmando o número. Se o número for inválido, o Chatbot informa ao usuário que o número de telefone não é válido.

O formato esperado é (+NN) NNN-NNN-NNN, em que N é um dígito.

"chatbot my phone number is (+34) 659-771-594" | check_phone_number
# => "Thanks! Your phone number is OK."
"chatbot, call me at 659-771-594" | check_phone_number
# => "Oops, it seems like I can't reach out to 659-771-594."

O Chatbot é um software muito curioso. Apesar de conseguir pesquisar na internet sobre um assunto específico, ele gosta de perguntar aos usuários sobre sites legais para visitar e encontrar informações relevantes.

Exemplo de conversa:

Chatbot: Ei, Usuário, eu gostaria de aprender a programar em JavaScript. Você conhece algum site legal onde eu poderia aprender?

Usuário: Eu aprendi muito no exercism.org, tem muita coisa boa lá.

Implemente a função get_domains, que retorna um array de domínios de sites.

"I learned a lot from exercism.org and google.com" | get_domains
# => ["exercism.org", "google.com"]

5. Cumprimente o usuário

Um Chatbot educado fala com os usuários pelo nome. Quando um usuário se apresenta, nosso Chatbot detecta o nome e responde com uma saudação amigável.

Escreva a função nice_to_meet_you.

Se a string de entrada contiver "My name is Someone.", capture o nome e retorne a string "Nice to meet you, Someone.".

"My name is Jean-Luc" | nice_to_meet_you
# => "Nice to meet you, Jean-Luc"

6. Análise de CSV bem simples

Cedendo à mania de adicionar cada vez mais funcionalidades, vamos adicionar uma função de análise de CSV ao Chatbot.

Implemente a função parse_csv, que recebe uma string e retorna um array com os campos resultantes. O separador de campos deve ser "vírgula mais espaço em branco opcional".

Não vamos nos preocupar com os casos limite do formato CSV (como campos que contêm vírgulas).

"first, second,third,   fourth" | parse_csv
# => ["first", "second", "third", "fourth"]
Editar via GitHub O link abre em uma nova janela ou aba
jq Exercism

Tudo pronto para começar Chatbot regular?

Crie sua conta no Exercism para aprender e dominar jq com 12 conceitos75 exercícios e mentoria humana de verdade, tudo de graça.