Tracks
/
jq
jq
/
Ejercicios
/
Chatbot con expresiones regulares
Chatbot con expresiones regulares

Chatbot con expresiones regulares

Ejercicio de aprendizaje

Introducción

Expresiones regulares

Las expresiones regulares (regexes) son secuencias de caracteres que especifican un patrón de búsqueda en un texto.

Aprender la sintaxis de las expresiones regulares queda fuera del alcance de este tema. Nos centraremos en las expresiones que jq ofrece para usar regexes.

Variante de regex

Distintas herramientas implementan distintas versiones de las expresiones regulares. jq incorpora la librería de regex Oniguruma, que es en gran medida compatible con las regexes de Perl v5.8.

La sintaxis específica que usa jq se puede encontrar en el repositorio de GitHub de Oniguruma.

Caution

jq no tiene ninguna sintaxis especial para las expresiones regulares. Simplemente se expresan como strings. Eso significa que las barras invertidas de la expresión regular se deben escapar dentro del string.

Por ejemplo, la clase de caracteres de dígitos (\d) se debe escribir como "\\d".

Funciones de regex

Las expresiones regulares en jq se limitan a un conjunto de filtros.

Coincidencia simple

Cuando necesitas saber si un string coincide con un patrón, usa el filtro test.

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

Este filtro devuelve un resultado Boolean.

"Hello World!" | test("W")    # => true
"Goodbye Mars" | test("W")    # => false
Información sobre la coincidencia

Cuando necesitas extraer el substring que realmente coincidió con el patrón, usa el filtro match.

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

Este filtro devuelve:

  • nada si no hubo coincidencia, o
  • un objeto con varias propiedades si hubo coincidencia.

Este ejemplo busca dos vocales idénticas consecutivas usando la sintaxis de referencia inversa, \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
#        }
#      ]
#    }

El filtro match devuelve un objeto por cada coincidencia. Este ejemplo muestra el flag "g" en acción para encontrar todas las vocales.

"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 capturados

De forma similar al filtro match, el filtro capture devuelve un objeto si hubo coincidencia.

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

El objeto devuelto es un mapeo de las capturas con nombre.

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

El filtro scan es similar a match con el flag "g".

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

scan emite un flujo de substrings.

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

Usa el constructor de array [...] para capturar los substrings.

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

Si conoces las partes del string que quieres conservar, usa match o scan. Si conoces las partes que quieres descartar, usa split.

STRING | split(REGEX; FLAGS)
Caution

El filtro split de aridad 1 trata su argumento como un string fijo.

Para usar una regex con split, debes proporcionar el segundo argumento; no pasa nada por usar un string vacío.

Un ejemplo que divide un string por espacios en blanco arbitrarios.

"first   second           third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
Sustituciones

Los filtros sub y gsub pueden transformar el string de entrada, reemplazando las partes del string que coinciden con un string de reemplazo.

Para reemplazar solo la primera coincidencia, usa sub. Para reemplazar todas las coincidencias, usa 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."

El texto de reemplazo puede referirse a los substrings que coinciden; usa capturas con nombre e interpolación 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

En todos los filtros anteriores, FLAGS es un string formado por cero o más de los flags admitidos.

  • g - Búsqueda global (encuentra todas las coincidencias, no solo la primera)
  • i - Búsqueda sin distinguir mayúsculas y minúsculas
  • m - Modo multilínea ('.' coincide con los saltos de línea)
  • n - Ignora las coincidencias vacías
  • p - Se activan los modos s y m a la vez
  • s - Modo de una sola línea ('^' -> '\A', '$' -> '\Z')
  • l - Encuentra las coincidencias más largas posibles
  • x - Formato de regex extendido (ignora los espacios en blanco y los comentarios)

Por ejemplo

"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")

Instrucciones

Te contrataron como especialista en expresiones regulares en una empresa que está desarrollando un Chatbot.

Está en una fase de desarrollo muy básica. Tu misión es usar expresiones regulares para mejorar la capacidad del Chatbot de entender y generar lenguaje natural.

1. Verifica si un comando es válido

Además de ser inteligente, el Chatbot también es un asistente leal. Para preguntarle algo al Chatbot, el usuario debe decir la palabra «Chatbot» en la primera posición del comando. No importa si la palabra clave está en MAYÚSCULAS o minúsculas. Lo importante es la posición de la palabra.

Implementa la función is_valid_command, que ayuda al Chatbot a reconocer cuándo el usuario le está dando un 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. Elimina los emojis cifrados

Al Chatbot le cuesta entender cómo las personas usan los emojis para expresar sus emociones. Cuando el Chatbot recibe mensajes de los usuarios, cada emoji se representa como el string «emoji» seguido de un número de id.

Implementa el método remove_emoji, que recibe un string y elimina todos los emojis a lo largo del mensaje.

Las líneas que no contengan emojis se deben devolver sin modificaciones. Solo elimina el string del emoji. No ajustes los espacios en blanco.

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

3. Verifica si un número de teléfono es válido

En algún momento de la interacción con el Chatbot, el usuario proporcionará un número de teléfono. El Chatbot solo puede llamar a un número con un formato específico.

Implementa la función check_phone_number.

Si el número es válido, el Chatbot responde con un mensaje que le agradece al usuario y confirma el número. Si el número no es válido, el Chatbot le informa al usuario que el número de teléfono no es válido.

El formato esperado es (+NN) NNN-NNN-NNN, donde N es un 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."

4. Obtén el enlace de un sitio web

El Chatbot es un programa realmente curioso. Aunque puede buscar en internet sobre un tema en particular, le gusta preguntarles a los usuarios por sitios web interesantes que visitar para encontrar información relevante.

Conversación de ejemplo:

Chatbot: Hola, usuario. Me gustaría aprender a programar en JavaScript. ¿Conoces algún sitio web interesante donde pueda aprender?

Usuario: Aprendí mucho en exercism.org, hay un montón de cosas geniales ahí.

Implementa la función get_domains, que devuelve un array de dominios de sitios web.

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

5. Saluda al usuario

Un Chatbot educado se dirige a los usuarios por su nombre. Cuando un usuario se presenta, nuestro Chatbot detecta su nombre y responde con un saludo amistoso.

Escribe la función nice_to_meet_you.

Si el string de entrada contiene «My name is Someone.», captura el nombre y devuelve el string «Nice to meet you, Someone.».

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

6. Análisis de CSV muy sencillo

Cediendo a la tentación de agregar cada vez más funcionalidades, vamos a agregarle una función de análisis de CSV al Chatbot.

Implementa la función parse_csv, que recibe un string y devuelve un array con los campos resultantes. El separador de campos debe ser «coma más espacio en blanco opcional».

No nos vamos a preocupar por los casos límite del formato CSV (como campos que contengan comas).

"first, second,third,   fourth" | parse_csv
# => ["first", "second", "third", "fourth"]
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
jq Exercism

¿Todo listo para empezar Chatbot con expresiones regulares?

Regístrate en Exercism para aprender y dominar jq con 12 conceptos75 ejercicios y mentoría humana real, todo gratis.