Rutas
/
Julia
Julia
/
Ejercicios
/
Niveles de registro
Niveles de registro

Niveles de registro

Ejercicio de aprendizaje

Introducción

Un String en Julia es una cadena de caracteres ordenada. Como señala el manual: «Por supuesto, la verdadera dificultad llega cuando uno se pregunta qué es un carácter».

Gran parte de esta complejidad se tratará con más detalle en el concepto Chars, pero en resumen:

  • Todos los strings en Julia son Unicode.
  • Los strings que además son ASCII (letras inglesas de la A a la Z en mayúsculas y minúsculas, dígitos y algunos caracteres de puntuación) son fáciles de manejar.
  • La mayoría de los demás sistemas de escritura del mundo son posibles de manejar, pero requieren más cuidado.

Literales de string

Por lo general, los strings se encierran entre comillas dobles " ". Las comillas simples ' ' solo se usan para Chars.

También es posible usar comillas dobles triples """ """, lo que permite usar " dentro del string sin escaparlo. Más importante aún, la indentación adicional no deseada se elimina de los strings multilínea.

julia> """
       Multiline
       
       """
"Multiline\nString\n"

Escapar caracteres

Como la mayoría de los lenguajes desde C en adelante, Julia usa una barra invertida \ para escapar:

  1. Para introducir caracteres no imprimibles, como el salto de línea \n del ejemplo anterior.
  2. Para proteger caracteres que, de otro modo, tendrían un significado especial, como \$ (ver más abajo).

Indexación

En principio, los strings se pueden indexar exactamente igual que los vectores:

julia> s[1], s[5]
('J', 'a')

Esto funciona claramente para un string ASCII como «Julia», pero ignora la mayoría de los idiomas del mundo.

Pensemos en uno de los caracteres de Beowulf, escrito en inglés antiguo hace unos 15 siglos.

«Hrōðgār» tiene claramente 7 caracteres, pero no todos son ASCII.

julia> king = "Hrōðgār"
"Hrōðgār"

julia> length(king)
7

julia> isascii(king)
false

# collect() converts to a Char vector:
julia> collect(king)
7-element Vector{Char}:
 'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
 'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)
 'ō': Unicode U+014D (category Ll: Letter, lowercase)
 'ð': Unicode U+00F0 (category Ll: Letter, lowercase)
 'g': ASCII/Unicode U+0067 (category Ll: Letter, lowercase)
 'ā': Unicode U+0101 (category Ll: Letter, lowercase)
 'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)

Indexar un string como este empieza bien, pero en algún momento se rompe:

julia> king[3]
'ō': Unicode U+014D (category Ll: Letter, lowercase)

julia> king[4]
ERROR: StringIndexError: invalid index [4], valid nearby indices [3]=>'ō', [5]=>'ð'

El problema es que indexar así cuenta bytes, que en los strings no ASCII no tienen una relación 1:1 con los caracteres. Un carácter Unicode/UTF-8 puede necesitar hasta 4 bytes para representarse, y el concepto Chars profundizará en esto.

Construir strings

Concatenación

Muchos lenguajes usan + como operador de concatenación de strings, pero Julia no. Al menos, el mensaje de error es útil.

julia> "s1 " + "s2"
ERROR: MethodError: no method matching +(::String, ::String)
The function `+` exists, but no method is defined for this combination of argument types.
String concatenation is performed with * 

julia> "s1 " * "s2"
"s1 s2"

La función string() concatena un número arbitrario de strings.

julia> string("lots ", "of ", "bits")
"lots of bits"

La función join() hace lo mismo y, además, toma un vector de strings y los concatena con un separador determinado (que de forma predeterminada es "").

julia> join(["lots", "of", "bits"], "_")
"lots_of_bits"

Ten en cuenta que los strings son inmutables y que la concatenación implica copiar. Por lo tanto, las concatenaciones repetidas dentro de un bucle son ineficientes y es mejor reunir los fragmentos en un vector y unirlos todos al final.

Repetición

Sencillo:

julia> repeat("Abc", 5)
"AbcAbcAbcAbcAbc"

Interpolación

Muchos lenguajes tienen una forma de insertar valores calculados en los strings, aunque hay un desacuerdo extraordinario sobre la mejor sintaxis que usar.

En Julia, los valores interpolados van precedidos de $.

julia> myvar = 42
42

julia> "myvar evaluates to $myvar"
"myvar evaluates to 42"

julia> r = 5.3
5.3

julia> "A circle of radius $r has area $(π * r^2)"
"A circle of radius 5.3 has area 88.24733763933729"

Los paréntesis son obligatorios cuando omitirlos resultaría ambiguo, pero opcionales para un único identificador seguido de un espacio en blanco.

Para incluir un $ en el string, escápalo como \$.

Ten en cuenta la limitación de que la interpolación no permite, por ahora, dar formato, como el número de decimales que mostrar.

@sprintf()

Para un control más preciso sobre el ensamblado de strings, Julia copia la función sprintf de C (y de varios lenguajes posteriores: Wikipedia enumera unos 30).

En Julia, esto se implementa como una macro dentro del módulo Printf, de ahí el prefijo @.

julia> using Printf

julia> r = 5.3
5.3

julia> @sprintf("A circle of radius %.1f has area %.2f", r, π * r^2)
"A circle of radius 5.3 has area 88.25"

Funciones para strings

Por simplificar, esta sección se centrará en las funciones que devuelven un string nuevo y dejan la entrada sin cambios. Muchas tienen una equivalente, nombrada con el sufijo !, que modifica la entrada in situ.

Longitud

¿Cuánto mide un string? A veces, depende de cómo cuentes.

El caso sencillo son los strings ASCII, en los que cada carácter ocupa 1 byte.

julia> string_asc = "Julia"
"Julia"

julia> length(string_asc)
5

julia> sizeof(string_asc)
5

La función length() devuelve el número de caracteres, y la función sizeof(), el número de bytes.

Esta distinción se vuelve importante con otros conjuntos de caracteres:

julia> king = "Hrōðgār"
"Hrōðgār"

julia> length(king)
7

julia> sizeof(king)
10

Dividir

Una operación habitual en programación es tomar un string largo y dividirlo en trozos, a partir de un string separador de uno o más caracteres.

Por lo general, usamos la función split(). El separador es, de forma predeterminada, (cualquier) espacio en blanco, pero se pueden especificar otros.

julia> split("my little string")
3-element Vector{SubString{String}}:
 "my"
 "little"
 "string"

julia> split("My_snake_case_string", "_")
4-element Vector{SubString{String}}:
 "My"
 "snake"
 "case"
 "string"

Estos ejemplos dividen un string en strings más pequeños. Dividir un string en caracteres es una operación distinta.

Buscar y reemplazar

¿Contiene un string una subcadena determinada? Curiosamente, hay dos funciones para comprobarlo: occursin() y contains() solo se diferencian en el orden de sus argumentos.

julia> needle = "Julia"
"Julia"

julia> haystack = "Python, Julia and R"
"Python, Julia and R"

julia> occursin(needle, haystack)
true

julia> contains(haystack, needle)
true

Para ser más específicos con la posición:

julia> startswith(haystack, "Python")
true

julia> endswith(haystack, "Python")
false

Para obtener los índices inicio:fin de una subcadena, tenemos varias funciones:

  • findfirst()
  • findlast()
  • findprev()
  • findnext()
julia> findfirst(needle, haystack)
9:13

Para reemplazar subcadenas, enumera los cambios con la sintaxis x => y.

julia> replace("abc", "a"=>"b", "b"=>"c", "c"=>"a")
"bca"

Desde Julia 1.7, la función replace puede aceptar un número arbitrario de cambios, con la garantía de que ningún carácter se cambiará más de una vez durante el reemplazo.

La sintaxis x => y se llama pair, un tipo importante en Julia que se trata con más detalle en el concepto Dicts and Pairs.

Espacios en blanco

A menudo necesitamos eliminar los espacios en blanco iniciales o finales de un string.

Las funciones para esto son lstrip() (izquierda), rstrip() (derecha) y strip() (ambas).

julia> strip("\n\t  useful_bit\n\n")
"useful_bit"

Cambiar mayúsculas y minúsculas

Estas funciones tienen nombres bastante autoexplicativos:

julia> s = repeat("aBcD ", 5)
"aBcD aBcD aBcD aBcD aBcD "

julia> uppercase(s)
"ABCD ABCD ABCD ABCD ABCD "

julia> lowercase(s)
"abcd abcd abcd abcd abcd "

julia> titlecase(s)  # Initial letter of each word uppercase
"Abcd Abcd Abcd Abcd Abcd "

Instrucciones

En este ejercicio vas a procesar líneas de registro.

Cada línea de registro es un string con el siguiente formato: "[<LEVEL>]: <MESSAGE>".

Hay tres niveles de registro distintos:

  • INFO
  • WARNING
  • ERROR

Tienes tres tareas, y cada una recibe una línea de registro y te pide hacer algo con ella.

1. Obtén el mensaje de una línea de registro

Implementa la función message para que devuelva el mensaje de una línea de registro:

julia> message("[ERROR]: Invalid operation")
"Invalid operation"

Hay que eliminar cualquier espacio en blanco sobrante al principio o al final:

julia> message("[WARNING]:  Disk almost full\r\n")
"Disk almost full"

2. Obtén el nivel de registro de una línea de registro

Implementa la función log_level para que devuelva el nivel de registro de una línea de registro, que debe devolverse en minúsculas:

julia> log_level("[ERROR]: Invalid operation")
"error"

3. Cambia el formato de una línea de registro

Implementa la función reformat, que cambia el formato de la línea de registro colocando primero el mensaje y después el nivel de registro entre paréntesis:

julia> reformat("[INFO]: Operation completed")
"Operation completed (info)"

Nota: Todos los strings de este ejercicio están en inglés y se limitan al conjunto de caracteres ASCII. Los conceptos posteriores te darán la oportunidad de trabajar con caracteres Unicode.

Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Julia Exercism

¿Listo para empezar Niveles de registro?

Regístrate en Exercism para aprender y dominar Julia con 35 conceptos128 ejercicios y mentoría humana real, todo gratis.