Clase String
Como vimos al tratar sobre los tipos básicos en Kotlin, el tipo String hace referencia a un tipo de dato inmutable formado por una sucesión de caracteres a los que se puede acceder por su índice. En esta entrada vamos a desarrollar y ampliar algunos conceptos sobre este tipo de dato y veremos diversas maneras de trabajar con ellos.
La clase String representa una cadena o secuencia de caracteres, cuya instancia es creada entre comillas dobles y se conocen como strings literales. La propiedad length de la clase String devuelve un número entero que indica la longitud o número de caracteres de una secuencia (incluyendo espacios):
val str = "Hola, soy un String." println(str) println("str tiene " + str.length + " caracteres")
Un String está compuesto por caracteres a los que se puede acceder por su índice (empezando desde el cero):
println(str[3]) // a
Combinando la propiedad length con los índices podemos obtener el primer y último carácter de una secuencia:
val str = "Kotlin Doc" println (str[0]) // K println (str[str.length-1]) // c
También los podemos obtener con los métodos first() y last():
println(str.first()) // K println(str.last()) // c
Un String es inmutable pero se puede reasignar otro valor si la variable es declarada mutable con var:
var str = "hola" // str[0] = 'H' // ERROR str = "Adiós"
Como sobre cualquier secuencia, podemos recorrer e iterar sobre los elementos (caracteres) de un String:
val str = "abcd" for (c in str) { print(c) // abcd }
Otro ejemplo para recorrer un String:
val frase = "Kotlin" for (car in frase) { print("$car ") } println() frase.forEach { c -> print(String.format("%#x ", c.toByte())) } println() frase.forEachIndexed { idx, e -> println("caracter $idx: $e ") }
K o t l i n 0x4b 0x6f 0x74 0x6c 0x69 0x6e caracter 0: K caracter 1: o caracter 2: t caracter 3: l caracter 4: i caracter 5: n
Kotlin tiene dos tipos especiales de strings literales: las cadenas de caracteres de escape y las cadenas en bruto o sin formato.
Los caracteres de escape se escriben anteponiendo una barra invertida y realizan una operación específica. Las secuencias de escape admitidas son: \t, \b, \n, \r, \', \", \\, \$. Algunos ejemplos:
println("Línea 1 \nLínea 2 \nLínea 3") // nueva línea println("Uno\t\tDos\t\tTres") // tabulaciones: Uno Dos Tres println("Hola\bMundo") // retorno: HolMundo println("Hola\rMundo") // retorno de carro: Mundo println("Pepe dijo: \"Hola\"") // Pepe dijo: "Hola" println("home\\usuario\\descargas") // home\usuario\descargas
Una cadena en bruto está delimitada por comillas triples y no puede contener caracteres de escape:
val texto = """ Ejemplo "for": for (c in string) print(c) """ println(texto)
Ejemplo "for": for (c in string) print(c)
En estos casos podemos eliminar los espacios en blanco con la función trimMargin() y añadiendo un prefijo a cada línea:
val texto = """ |Ejemplo "for": |for (c in string) | print(c) """.trimMargin() println(texto)
Ejemplo "for": for (c in string) print(c)
Por defecto se utiliza el carácter | como prefijo de margen, pero podemos elegir otro carácter o caracteres y pasarlos como parámetro de trimMargin:
val texto = """ >Ejemplo "for": >for (c in string) > print(c) """.trimMargin(">") println(texto)
Otra opción es eliminar la sangría con trimIndent():
val soneto = """ Un soneto me manda hacer Violante, y en mi vida me he visto en tal aprieto: Catorce versos dicen que es soneto: Burla burlando van los tres delante. """ println(soneto.trimIndent())
Podemos concatenar String usando el operador +. Esto también funciona para concatenar String con valores de otros tipos siempre que el primer elemento en la expresión sea un String:
val letras = "abc" val masLetras = "def" val abecedario = letras + masLetras print(abecedario) // abcdef println() val letrasMasNum = "abc" + 1 println(letrasMasNum + masLetras) // abc1def
No obstante, en la mayoría de los casos es preferible utilizar plantillas de String antes que esta concatenación de String. Las plantillas de String son expresiones que se evalúan y cuyos resultados se interpolan dentro de la secuencia. Una expresión de plantilla se escribe siempre anteponiendo el signo de dólar ($) a una variable o a una expresión entre llaves:
val i = 10 println("i = $i") // i = 10 val nombre = "Juan" val edad = 43 println("$nombre tiene $edad años.") val letras = "abc" val masLetras = "def" println("$letras$masLetras") // abcdef val s = "abc" println("$s.length = ${s.length}") // abc.length = 3
Para comparar el contenido de dos String podemos usar el operador == o bien el método compareTo(). Este método compara dos objetos y devuelve cero si son iguales, un número negativo si el primer objeto es menor que el segundo, y un número positivo si el primero es mayor que el otro. Opcionalmente se puede usar con un segundo parámetro (ignoreCase) para omitir mayúsculas y minúsculas en la comparación:
val s1 = "Abc" val s2 = "abc" println(s1 == s2) // false println(s1.compareTo(s2, false)) // -32 println(s1.compareTo(s2)) // -32 (por defecto es false y se puede omitir) println("Ignorando mayúsculas") if (s1.compareTo(s2, true) == 0) { // 0 println("Secuencias iguales") } else { println("Secuencias distintas") }
Kotlin ofrece una amplia variedad de métodos para trabajar con String y realizar operaciones sobre ellos pero, ya que son inmutables, todas esas operaciones crean una nueva secuencia en lugar de modificar la secuencia original.
Kotlin tiene varios métodos para trabajar con la capitalización (mayúsculas y minúsculas) de una secuencia de caracteres:
val str = "homer Simpson" println(str.capitalize()) // Homer Simpson println(str.toUpperCase()) // HOMER SIMPSON println(str.toLowerCase()) // homer simpson println("Homer".decapitalize()) // homer
Kotlin distingue entre secuencias vacías y en blanco: una cadena vacía no tiene ningún carácter, mientras que una cadena en blanco contiene cualquier número de espacios en blanco, y existen métodos específicos para cada una:
val tab = "\t" val esp = " " val vac = "" println(tab.isEmpty()) // false println(tab.isBlank()) // true println(esp.isEmpty()) // false println(esp.isBlank()) // true println(vac.isEmpty()) // true println(vac.isBlank()) // true
También existen métodos para quitar caracteres de espacio en blanco de una cadena:
val str = " Ab cd\t" println("str tiene ${str.length} caracteres") // 7 val str1 = str.trimEnd() // elimina los espacios en blanco finales println(str1) // Ab cd println("str1 tiene ${str1.length} caracteres") // 6 caracteres val str2 = str.trimStart() // elimina los espacios en blanco iniciales println(str2) // Ab cd println("str2 tiene ${str2.length} caracteres") // 6 caracteres val str3 = str.trim() // elimina todos los espacios en blanco iniciales y finales println(str3) // Ab cd println("str3 tiene ${str3.length} caracteres") // 5 caracteres
El método filter() devuelve una cadena que contiene solo aquellos caracteres de la cadena original que coinciden con el argumento dado:
// función de extensión que devuelve true o false fun Char.esVocal(): Boolean { if (this == 'a' || this == 'e' || this == 'i' || this == 'o' || this == 'u' ) return true return false } /* la misma función también se puede escribir así: fun Char.esVocal(): Boolean = this == 'a' || this == 'e' || this == 'i' || this == 'o' || this == 'u' */ /* aunque yo prefiero así: fun Char.esVocal(): Boolean { when(this) { 'a', 'e', 'i', 'o', 'u' -> return true } return false } */ fun main() { val str = "La familia Simpson." val nVocales = str.filter { c -> c.toLowerCase().esVocal() } println("Hay ${nVocales.length} vocales: $nVocales") // Hay 7 vocales: aaiiaio }
También podemos comprobar si una secuencia empieza o termina por uno o varios caracteres con los métodos startsWith() y endsWith(), que devuelven true en caso positivo:
fun main() { val palabras = listOf( "tanque", "chicos", "turista", "doce", "pelota", "tulipán", "casas", "coche", "quizá", "sombrero", "tonel", "átomos", "hormiga" ) val palabrasTInicio = palabras.filter { e -> inicioT(e) } println(palabrasTInicio) // [tanque, turista, tulipán, tonel] val palabrasOsFinal = palabras.filter { e -> finalOs(e) } println(palabrasOsFinal) // [chicos, átomos] } fun inicioT(palabra: String) = palabra.startsWith("t") fun finalOs(palabra: String) = palabra.endsWith("os")
El método replace() devuelve una nueva cadena obtenida al reemplazar todas las ocurrencias de una determinada secuencia en una cadena por otra secuencia, sin modificar la cadena original:
val str = "x por x es igual a cuatro." val str2 = str.replace("x", "dos") println(str2) // dos por dos es igual a cuatro.
El método toString() sirve para proporcionar una representación tipo String de un objeto:
class Ciudad(private var nombre: String, private var poblacion: Int) { override fun toString(): String { return "Población de $nombre: ${String.format("%,d", poblacion)}" } } fun main() { val ciudades = listOf( Ciudad("Madrid", 3_223_334), Ciudad("Barcelona", 1_620_343), Ciudad("Albacete", 173_050) ) ciudades.forEach { e -> println(e) } }
Población de Madrid: 3.223.334 Población de Barcelona: 1.620.343 Población de Albacete: 173.050
También hay métodos para rellenar secuencias con un carácter específico:
val nums = intArrayOf(657, 122, 3245, 345, 99, 18) nums.toList().forEach { e -> println(e.toString().padStart(10, '.')) } // parámetros de padStart: (longitud, caracter de relleno)
.......657 .......122 ......3245 .......345 ........99 ........18
Por último, como hemos visto en un par de ejemplos en esta misma entrada, se puede dar formato de salida a un String utilizando String.format() con argumentos específicos utilizando el símbolo % seguido de un especificador según el tipo de datos a que se aplica (d: Int, s: String, b: Boolean, c: Character, f: Float, t: Date o Time...). En el siguiente código vamos a ver algunos ejemplos:
import java.util.Locale fun main() { val nombre = "Juan" println(String.format("Mi nombre es %s", nombre)) // Mi nombre es Juan val miFormato = "Mi nombre es %s" println(miFormato.format(nombre)) // Mi nombre es Juan // lo mismo con una plantilla de String println("Mi nombre es $nombre") // Mi nombre es Juan // especificadores de cadena y número println(String.format("%s = %d", "Resultado", 35)) // Resultado = 35 // formatea números largos con separador de miles println(String.format("%,d", 42125741)) // 42.125.741 println(String.format(Locale.ENGLISH, "%,d", 42125741)) // 42,125,741 println(String.format(Locale.GERMAN, "%,d", 42125741)) // 42.125.741 // especifica el ancho println(String.format("|%10d|", 93)) // | 93| // ancho y justificación a la izquierda println(String.format("|%-10d|", 93)) // |93 | // ancho y relleno de ceros println(String.format("|%010d|", 93)) // |0000000093| // incluye el signo + delante de los números positivos (el signo - simepre aparece en los negativos) println(String.format("%+d", 93)) // +93 // incluye un espacio solo delante de los números positivos println(String.format("|% d|", 93)) // | 93| println(String.format("|% d|", -93)) // |-93| // encierra los números negativos entre paréntesis y quita el signo - println(String.format("|%(d|", -36)) // |(36)| val pi = 3.14159265358979323 println(String.format("%g", pi)) // 3,14159 println(String.format("%.4g", pi)) // 3,142 println(String.format("%.2f", pi)) // 3,14 val decimal = 3.0 println(String.format("%f", decimal)) // 3,000000 // especifica longitud del campo println(String.format("|%20s|", "Hola Mundo")) // | Hola Mundo| // justificación a la izquierda println(String.format("|%-20s|", "Hola Mundo")) // |Hola Mundo | // especifica el número máximo de caracteres println(String.format("|%.4s|", "Hola Mundo")) // |Hola| // ancho y número máximo de caracteres println(String.format("|%20.4s|", "Hola Mundo")) // | Hola| }
Comentarios
Publicar un comentario