Tracks
/
Go
Go
/
Ejercicios
/
Análisis de archivos de registro
Análisis de archivos de registro

Análisis de archivos de registro

Ejercicio de aprendizaje

Introducción

El paquete regexp ofrece soporte para expresiones regulares en Go.

Sintaxis

La sintaxis de las expresiones regulares aceptadas es la misma sintaxis general que usan Perl, Python y otros lenguajes.

Tanto los patrones de búsqueda como los textos de entrada se interpretan como UTF-8.

Cuando usas comillas invertidas (`) para crear strings, las barras invertidas (\) no tienen ningún significado especial y no marcan el inicio de caracteres especiales como las tabulaciones \t o los saltos de línea \n:

"\t\n" // regular string literal with 2 characters: a tab and a newline
`\t\n`// raw string literal with 4 characters: two backslashes, a 't', and an 'n'

Por eso, conviene usar comillas invertidas para crear expresiones regulares, porque así no necesitas escapar las barras invertidas:

"\\" // string with a single backslash
`\\` // string with 2 backslashes

Compilar patrones: el tipo RegExp

Para usar una expresión regular, primero debemos compilar el patrón de string. Compilar aquí significa tomar el patrón de string de la expresión regular y convertirlo en una representación interna con la que es más fácil trabajar. Solo necesitamos compilar cada patrón una vez; después de eso, podemos usar la versión compilada de la expresión regular muchas veces. El tipo regexp.Regexp representa una expresión regular compilada. Podemos compilar un patrón de string en un regexp.Regexp usando la función regexp.Compile. Esta función devuelve nil y un error si la compilación falló:

re, err := regexp.Compile(`(a|b)+`)
fmt.Println(re, err) // => (a|b)+ <nil>
re, err = regexp.Compile(`a|b)+`)
fmt.Println(re, err) // => <nil> error parsing regexp: unexpected ): `a|b)+`

La función MustCompile es una alternativa conveniente a Compile:

re = regexp.MustCompile(`[a-z]+\d*`)

Con esta función, no necesitas manejar ningún error.

Caution

MustCompile solo debe usarse cuando sabemos con certeza que el patrón sí compila, porque de lo contrario el programa entrará en pánico.

Métodos de las expresiones regulares

Hay 16 métodos de Regexp que buscan coincidencias con una expresión regular e identifican el texto coincidente. Sus nombres coinciden con esta expresión regular:

Find(All)?(String)?(Submatch)?(Index)?
  • Si All está presente, el método busca coincidencias sucesivas y no superpuestas de toda la expresión.
  • Si String está presente, el argumento es un string; de lo contrario, es un slice de bytes; los valores de retorno se ajustan según corresponda.
  • Si Submatch está presente, el valor de retorno es un slice que identifica las subcoincidencias sucesivas de la expresión.
  • Si Index está presente, las coincidencias y subcoincidencias se identifican mediante pares de índices de bytes dentro del string de entrada.

También hay métodos para:

  • reemplazar coincidencias de expresiones regulares con strings de reemplazo, y
  • dividir strings separados por expresiones regulares.

En total, el paquete regexp define más de 40 funciones y métodos. A continuación, mostraremos el uso de algunos métodos. Consulta la documentación de la API para ver los detalles de estas y otras funciones.

Ejemplos de MatchString

El método MatchString indica si un string contiene alguna coincidencia de una expresión regular.

re = regexp.MustCompile(`[a-z]+\d*`)
b = re.MatchString("[a12]")       // => true
b = re.MatchString("12abc34(ef)") // => true
b = re.MatchString(" abc!")       // => true
b = re.MatchString("123 456")     // => false

Ejemplos de FindString

El método FindString devuelve un string con el texto de la coincidencia más a la izquierda de la expresión regular.

re = regexp.MustCompile(`[a-z]+\d*`)
s = re.FindString("[a12]")       // => "a12"
s = re.FindString("12abc34(ef)") // => "abc34"
s = re.FindString(" abc!")       // => "abc"
s = re.FindString("123 456")     // => ""

Ejemplos de FindStringSubmatch

El método FindStringSubmatch devuelve un slice de strings con el texto de la coincidencia más a la izquierda de la expresión regular y las coincidencias, si las hay, de sus subexpresiones. Esto se puede usar para identificar los strings que coinciden con grupos de captura. Un valor de retorno nil indica que no hay coincidencia.

re = regexp.MustCompile(`[a-z]+(\d*)`)
sl = re.FindStringSubmatch("[a12]")       // => []string{"a12","12"}
sl = re.FindStringSubmatch("12abc34(ef)") // => []string{"abc34","34"}
sl = re.FindStringSubmatch(" abc!")       // => []string{"abc",""}
sl = re.FindStringSubmatch("123 456")     // => <nil>

Ejemplos de ReplaceAllString

El método re.ReplaceAllString(src,repl) devuelve una copia de src, reemplazando las coincidencias de la expresión regular re con el string de reemplazo repl.

re = regexp.MustCompile(`[a-z]+\d*`)
s = re.ReplaceAllString("[a12]", "X")       // => "[X]"
s = re.ReplaceAllString("12abc34(ef)", "X") // => "12X(X)"
s = re.ReplaceAllString(" abc!", "X")       // => " X!"
s = re.ReplaceAllString("123 456", "X")     // => "123 456"

Ejemplos de Split

El método re.Split(s,n) divide un texto s en substrings separados por la expresión y devuelve un slice con los substrings entre esas coincidencias de la expresión. El recuento n determina el número máximo de substrings a devolver. Si n<0, el método devuelve todos los substrings.

re = regexp.MustCompile(`[a-z]+\d*`)
sl = re.Split("[a12]", -1)      // => []string{"[","]"}
sl = re.Split("12abc34(ef)", 2) // => []string{"12","(ef)"}
sl = re.Split(" abc!", -1)      // => []string{" ","!"}
sl = re.Split("123 456", -1)    // => []string{"123 456"}

Instrucciones

Este ejercicio aborda el análisis de archivos de log.

Después de una revisión de seguridad reciente, te pidieron que limpiaras los archivos de log archivados de la organización.

Está garantizado que todos los strings que se pasan a las funciones no son nulos y no tienen espacios al principio ni al final.

1. Identifica las líneas de log corruptas

Necesitas hacerte una idea de cuántas líneas de log de tu archivo no cumplen con los estándares actuales. Crees que una prueba simple revela si una línea de log es válida. Para considerarse válida, una línea debe comenzar con uno de los siguientes strings:

  • [TRC]
  • [DBG]
  • [INF]
  • [WRN]
  • [ERR]
  • [FTL]

Implementa la función IsValidLine para que devuelva false si un string no es válido y true en caso contrario.

IsValidLine("[ERR] A good error here")
// => true
IsValidLine("Any old [ERR] text")
// => false
IsValidLine("[BOB] Any old text")
// => false

2. Divide la línea de log

Un equipo nuevo se unió a la organización y descubres que sus archivos de log usan un separador extraño para los «campos». En lugar de algo sensato como dos puntos «:», usan un string como «<--->» o «<=>» (porque es más bonito); de hecho, cualquier string cuyo primer carácter sea «<» y cuyo último carácter sea «>» y que tenga en medio cualquier combinación de los siguientes caracteres: «~», «*», «=» y «-».

Implementa la función SplitLogLine, que recibe una línea y devuelve un array de strings, cada uno de los cuales contiene un campo.

SplitLogLine("section 1<*>section 2<~~~>section 3")
// => []string{"section 1", "section 2", "section 3"},

3. Cuenta el número de líneas que contienen password en texto entre comillas

El equipo necesita conocer las referencias a contraseñas en texto entre comillas para poder examinarlas manualmente.

Implementa la función CountQuotedPasswords para darte una idea de la magnitud probable del trabajo manual.

Identifica las líneas de log donde el string «password», que puede estar en cualquier combinación de mayúsculas y minúsculas, está rodeado de comillas. Debes tener en cuenta la posibilidad de que haya contenido adicional entre las comillas, antes y después de «password». Cada línea contendrá como máximo dos comillas.

Las líneas que se pasan a la rutina pueden ser válidas o no según lo definido en la tarea 1. Las procesamos de la misma manera, sean válidas o no.

lines := []string{
    `[INF] passWord`, // contains 'password' but not surrounded by quotation marks
    `"passWord"`,  // count this one
    `[INF] User saw error message "Unexpected Error" on page load.`, // does not contain 'password'
    `[INF] The message "Please reset your password" was ignored by the user`, // count this one
}
// => 2

4. Elimina los artefactos del log

Has descubierto que algún procesamiento anterior de los logs ha estado dispersando por todos los logs el texto «end-of-line» seguido de un número de línea (sin un espacio intermedio).

Implementa la función RemoveEndOfLineText para que reciba un string, elimine el texto end-of-line y devuelva un string «limpio».

Las líneas que no contengan el texto end-of-line deben devolverse sin modificaciones.

Solo elimina el string end-of-line. No intentes ajustar los espacios en blanco.

RemoveEndOfLineText("[INF] end-of-line23033 Network Failure end-of-line27")
// => "[INF]  Network Failure "

5. Etiqueta las líneas con nombres de usuario

Te has dado cuenta de que algunas de las líneas de log incluyen oraciones que se refieren a usuarios. Estas oraciones siempre contienen el string "User", seguido de uno o más espacios y luego un nombre de usuario. Decides etiquetar esas líneas.

Implementa una función TagWithUserName que procese líneas de log:

  • Las líneas que no contengan el string "User " permanecen sin cambios.
  • En las líneas que contengan el string "User ", antepón a la línea [USR] seguido del nombre de usuario.

Por ejemplo:

result := TagWithUserName([]string{
    "[WRN] User James123 has exceeded storage space.",
	"[WRN] Host down. User   Michelle4 lost connection.",
	"[INF] Users can login again after 23:00.",
	"[DBG] We need to check that user names are at least 6 chars long.",
})
// => []string {
//  "[USR] James123 [WRN] User James123 has exceeded storage space.",
//  "[USR] Michelle4 [WRN] Host down. User   Michelle4 lost connection.",
//  "[INF] Users can login again after 23:00.",
//  "[DBG] We need to check that user names are at least 6 chars long."
// }

Puedes asumir que:

  • Los nombres de usuario van seguidos de al menos un carácter de espacio en blanco en el log.
  • Hay como máximo una aparición del string "User " en cada línea.
  • Los nombres de usuario son strings no vacíos que no contienen espacios en blanco.
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Go Exercism

¿Todo listo para empezar Análisis de archivos de registro?

Regístrate en Exercism para aprender y dominar Go con 34 conceptos165 ejercicios y mentoría humana real, todo gratis.