Saltar al contenido principal

Cadenas

Una cadena es un texto: un nombre, un mensaje, una línea que lees de un fichero o que llega por la red. Se escribe entre comillas y se guarda en una variable como cualquier otro valor. Lo general de los tipos de datos está en Tipos de datos.

Las cadenas no necesitan use: sus métodos están siempre, y se llaman con un punto sobre una variable, como nombre.upper(). Una cadena no cambia nunca: cada método devuelve una cadena nueva y deja la original como estaba.

var nombre = "Ana"
var grito = nombre.upper()
println(grito) // ANA
println(nombre) // Ana

Las cadenas funcionan igual en Windows y en todas las placas.

Escribir cadenas​

Una cadena va entre comillas dobles o simples. Dentro de unas puedes poner las otras. "" es la cadena vacía.

var saludo = "Hola"
var cita = 'Di "hola"'
var ingles = "It's"
var vacia = ""
println(cita) // Di "hola"
println(ingles) // It's

Dentro de las comillas, la barra \ empieza un escape:

EscapeQué pone en la cadena
\nUn salto de línea. Son dos caracteres: el retorno de carro y el salto de línea (CR LF).
\rUn retorno de carro (CR).
\tUn tabulador.
\\Una barra.

Ninguna otra combinación es un escape: la barra se queda en la cadena, con el carácter que la siga. "\"" no es una comilla: es una cadena de dos caracteres, la barra y la comilla. Para escribir comillas dobles, abre la cadena con comillas simples, y al revés.

Una cadena puede seguir en la línea siguiente del programa. Cada salto de línea del programa se convierte en un espacio:

var aviso = "Pulsa A para saltar
y B para disparar"
println(aviso) // Pulsa A para saltar y B para disparar

Leer los caracteres​

texto[i] devuelve el carácter de la posición i, como una cadena de uno. La primera posición es la 0, y la última, texto.size() - 1:

var palabra = "gato"
println(palabra[0]) // g
println(palabra[palabra.size() - 1]) // o
  • Una posición negativa o mayor que la última lanza ERR_INDEX_OUT_OF_RANGE. No hay posiciones contadas desde el final.
  • La posición tiene que ser un entero. Con un decimal, aunque sea 1.0, lanza ERR_TYPE_MISMATCH.

Para recorrer una cadena, usa foreach, que da un carácter en cada vuelta, o un for con las posiciones:

var palabra = "gato"
foreach (var letra in palabra)
print(letra, " ")
end
println("")
for (var i = palabra.size() - 1; i >= 0; i--)
print(palabra[i])
end
println("")
g a t o
otag

Cambiar un carácter​

texto[i] = "x" cambia el carácter de la posición i. Como las cadenas no cambian, en realidad se crea una cadena nueva y se guarda en la variable. Otra variable que tuviera el mismo texto se queda como estaba:

var a = "gato"
var b = a
a[0] = "p"
println(a) // pato
println(b) // gato

Solo cuenta el primer carácter de lo que asignas: a[1] = "XY" pone una X. Lo que asignas tiene que ser una cadena con algún carácter: con otra cosa, como un número, lanza ERR_TYPE_MISMATCH, y con una cadena vacía, ERR_INVALID_VALUE. En los dos casos la cadena se queda como estaba.

Sacar un trozo​

No hay un método que saque un trozo de una cadena. Se hace con un bucle:

func trozo(texto, desde, hasta)
var resultado = ""
for (var i = desde; i < hasta; i++)
resultado = resultado + texto[i]
end
return resultado
end

var frase = "Hola mundo"
println(trozo(frase, 5, 10)) // mundo

Si el texto tiene un separador, como una coma, es más fácil partirlo con split.

Unir cadenas​

+ une dos cadenas:

var nombre = "Ana"
var saludo = "¡Hola, " + nombre + "!"
println(saludo) // ¡Hola, Ana!

+ solo une una cadena con otra. Una cadena más un número, un booleano, nil o una lista lanza ERR_TYPE_MISMATCH, con el mensaje Unsupported operand types for operator '+'.:

var vidas = 3
try
var texto = "Vidas: " + vidas
catch (var e)
println(e.message) // Unsupported operand types for operator '+'.
end

Para unir un número, conviértelo antes en cadena con su método toString() (los métodos de los números están en Tipos de datos). Si solo quieres imprimirlo, no hace falta: print y println admiten varios argumentos y los escriben uno detrás de otro (ver Librería estándar).

var vidas = 3
var tiempo = 12.5
var linea = "Vidas: " + vidas.toString() + " Tiempo: " + tiempo.toString()
println(linea) // Vidas: 3 Tiempo: 12.5
println("Vidas: ", vidas) // Vidas: 3
  • El toString() de un decimal da como mucho 15 cifras: 0.1 + 0.2 sale 0.3. Un decimal suelto en println sale con las cifras justas para que sea el mismo número: 0.30000000000000004.
  • Los booleanos no tienen toString(): usa un if con las dos cadenas.
  • Para unir todos los elementos de una lista, usa join.

Comparar cadenas​

== y != comparan el texto, carácter a carácter, y distinguen las mayúsculas de las minúsculas. Para comparar sin mirarlas, pasa antes las dos a minúsculas:

var a = "Hola"
var b = "hola"
println(a == b) // false
var la = a.lower()
var lb = b.lower()
println(la == lb) // true

<, >, <= y >= no comparan texto: entre dos cadenas distintas dan siempre false, y <= y >= dan true solo si son iguales. Para ordenar cadenas, mételas en una lista y usa sort().

Letras con tilde​

Las cadenas guardan el texto en UTF-8: una letra con tilde, la ñ o un símbolo como € ocupan dos bytes o más, y los métodos cuentan bytes, no letras.

var palabra = "año"
println(palabra.size()) // 4
var mayusculas = palabra.upper()
println(mayusculas) // AñO
  • size() y len() cuentan bytes, y find() devuelve una posición en bytes.
  • texto[i], foreach y split("") van byte a byte: una ñ sale partida en dos trozos que no se pueden imprimir por separado.
  • upper() y lower() solo cambian las letras de la A a la Z. Las letras con tilde y la ñ se quedan como están.
  • isAlpha() e isAlnum() dan false si hay una letra con tilde o una ñ.
  • ==, contains(), count(), replace() y split() con un separador funcionan bien con cualquier texto.

Métodos​

MétodoQué haceDevuelve
size()Cuenta los bytes.Entero
find(subcadena, n)Busca una subcadena.Entero: la posición, o -1
contains(subcadena)Mira si la contiene.true o false
count(subcadena)Cuenta cuántas veces aparece.Entero
startsWith(inicio)Mira si empieza así.true o false
endsWith(final)Mira si acaba así.true o false
lower()Pasa a minúsculas.Cadena nueva
upper()Pasa a mayúsculas.Cadena nueva
strip()Quita los espacios de los dos lados.Cadena nueva
leftStrip()Quita los espacios del principio.Cadena nueva
rightStrip()Quita los espacios del final.Cadena nueva
replace(buscar, poner)Cambia un texto por otro.Cadena nueva
split(separador)Parte la cadena por un separador.Lista de cadenas
join(lista)Une los elementos de una lista, con la cadena entre ellos.Cadena nueva
isAlpha()Mira si todo son letras.true o false
isDigit()Mira si todo son cifras.true o false
isAlnum()Mira si todo son letras o cifras.true o false
toInt()Convierte a entero.Entero
toFloat()Convierte a decimal.Decimal, o un valor de error
toBool()Convierte a booleano.true o false

Errores comunes a todos:

  • Con más o menos argumentos de los suyos lanzan ERR_INVALID_ARG_NUMBER, y con un argumento de otro tipo, ERR_TYPE_MISMATCH.
  • El nombre sin paréntesis (texto.size) lanza ERR_INVALID_OPERATION, y un método que no existe, ERR_ATTRIBUTE_NOT_FOUND.
  • Son excepciones: se capturan con try y catch (ver Errores y excepciones).

texto.size()​

Devuelve el número de bytes de la cadena: el número de letras si no tiene tildes. Es lo mismo que len(texto).

var nombre = "Ana"
var vacia = ""
println(nombre.size()) // 3
println(vacia.size()) // 0

texto.find(subcadena, n)​

Busca subcadena y devuelve la posición donde empieza, contando desde 0, o -1 si no está. n es opcional: el número de la aparición que buscas. Sin él, busca la primera.

var frase = "Hola mundo"
println(frase.find("mundo")) // 5
println(frase.find("o")) // 1
println(frase.find("o", 2)) // 9
println(frase.find("x")) // -1
  • Distingue las mayúsculas de las minúsculas.
  • Con una subcadena vacía devuelve 0.
  • subcadena tiene que ser una cadena, y n, un entero. Si no, lanza ERR_TYPE_MISMATCH.
  • n empieza en 1: con 0 o un número negativo lanza ERR_INVALID_VALUE.

texto.contains(subcadena)​

Devuelve true si subcadena está en el texto. Distingue las mayúsculas, y con una subcadena vacía da true.

var frase = "Hola mundo"
println(frase.contains("mundo")) // true
println(frase.contains("Mundo")) // false

texto.count(subcadena)​

Cuenta cuántas veces aparece subcadena, sin contar dos veces las letras que se solapan: en "banana", "ana" aparece una vez. Con una subcadena vacía devuelve 0.

var fruta = "banana"
println(fruta.count("a")) // 3
println(fruta.count("ana")) // 1

texto.startsWith(inicio)​

Devuelve true si el texto empieza por inicio. Distingue las mayúsculas, y con "" da true.

var fichero = "nivel3.map"
println(fichero.startsWith("nivel")) // true
println(fichero.startsWith("Nivel")) // false

texto.endsWith(final)​

Devuelve true si el texto acaba en final. Distingue las mayúsculas, y con "" da true.

var fichero = "nivel3.map"
println(fichero.endsWith(".map")) // true
println(fichero.endsWith(".MAP")) // false

texto.lower()​

Devuelve el texto en minúsculas. Solo cambia las letras de la A a la Z: las letras con tilde y la Ñ se quedan como están.

var titulo = "ÁRBOL Y ÑU"
println(titulo.lower()) // Árbol y Ñu

texto.upper()​

Devuelve el texto en mayúsculas. Solo cambia las letras de la a a la z.

var nombre = "Íñigo"
println(nombre.upper()) // ÍñIGO

texto.strip()​

Quita del principio y del final los espacios, los tabuladores y los saltos de línea. Los de en medio se quedan.

var entrada = "  hola   mundo \n"
var limpia = entrada.strip()
println("[", limpia, "]") // [hola mundo]

texto.leftStrip()​

Como strip(), pero solo por el principio.

var entrada = "   hola   "
println("[", entrada.leftStrip(), "]") // [hola ]

texto.rightStrip()​

Como strip(), pero solo por el final.

var entrada = "   hola   "
println("[", entrada.rightStrip(), "]") // [ hola]

texto.replace(buscar, poner)​

Devuelve el texto con cada aparición de buscar cambiada por poner. Si buscar no está, o es una cadena vacía, devuelve el mismo texto. Para quitar un texto, pon "" en poner.

var frase = "Hola mundo"
println(frase.replace("mundo", "JARU")) // Hola JARU
println(frase.replace("o", "0")) // H0la mund0
var hora = "12:30:05"
println(hora.replace(":", "")) // 123005

Los dos argumentos tienen que ser cadenas. Si no, lanza ERR_TYPE_MISMATCH.

texto.split(separador)​

Parte el texto por cada aparición de separador y devuelve una lista con los trozos, sin el separador.

var csv = "rojo,verde,azul"
var colores = csv.split(",")
println(colores) // [rojo, verde, azul]
println(colores.size()) // 3
  • Dos separadores seguidos, o uno al principio o al final, dan trozos vacíos: "a,,b," da cuatro trozos, "a", "", "b" y "".
  • Si el separador no está, la lista tiene un solo trozo: el texto entero. Una cadena vacía da una lista con una cadena vacía.
  • Con "" de separador, parte el texto byte a byte (ver Letras con tilde).
  • Para partir en líneas un texto que has escrito en el programa, usa "\n". Un texto que viene de fuera (de un fichero o de la red) puede traer los saltos de línea sin el retorno de carro, y entonces "\n" no lo parte: pártelo por chr(10) y quita a cada línea el retorno de carro que pueda llevar al final con strip().

separador.join(lista)​

Une los elementos de lista en una cadena, con el texto sobre el que lo llamas entre uno y otro. Como un método no se puede llamar sobre una cadena escrita entre comillas, guarda antes el separador en una variable:

var colores = ["rojo", "verde", "azul"]
var coma = ", "
println(coma.join(colores)) // rojo, verde, azul
var nada = ""
println(nada.join(colores)) // rojoverdeazul
  • Los elementos que no son cadenas se convierten: los números, como con toString(); los booleanos, en true o false; nil, en none; y una lista o un mapa, en Object.
  • Con una lista vacía devuelve "".
  • El argumento tiene que ser una lista (un array no vale). Si no, lanza ERR_TYPE_MISMATCH.

texto.isAlpha()​

Devuelve true si todos los caracteres son letras de la a a la z, en mayúscula o en minúscula. Una cadena vacía, un espacio, una letra con tilde o una ñ dan false.

var a = "Jaru"
var b = "año"
println(a.isAlpha()) // true
println(b.isAlpha()) // false

texto.isDigit()​

Devuelve true si todos los caracteres son cifras del 0 al 9. Un signo, un punto decimal o una cadena vacía dan false. Sirve para saber si un texto es un entero sin signo antes de convertirlo con toInt().

var a = "2026"
var b = "-12"
var c = "1.5"
println(a.isDigit()) // true
println(b.isDigit()) // false
println(c.isDigit()) // false

texto.isAlnum()​

Devuelve true si todos los caracteres son letras de la a a la z o cifras. Un _, un espacio, una letra con tilde o una cadena vacía dan false.

var a = "nivel3"
var b = "nivel_3"
println(a.isAlnum()) // true
println(b.isAlnum()) // false

texto.toInt()​

Convierte el texto en un entero.

var a = "42"
var b = "3.7"
var c = "0x1F"
println(a.toInt() + 1) // 43
println(b.toInt()) // 3
println(c.toInt()) // 31
  • Admite espacios al principio, un signo, decimales (se queda con la parte entera, sin redondear: "-3.7" da -3), hexadecimal con 0x y exponente ("1e3" da 1000).
  • Si el texto no es un número, está vacío o lleva algo detrás del número, aunque sea un espacio, lanza ERR_TYPE_MISMATCH con el mensaje Can not convert '…' to a number.
  • Si el número, sin su parte decimal, no cabe en un entero (más de 2.147.483.647 o menos de -2.147.483.648), o es inf o nan, también lanza ERR_TYPE_MISMATCH, con el mensaje Can not convert '…' to an integer: out of range (-2147483648 to 2147483647).
var texto = "12abc"
try
var n = texto.toInt()
println(n)
catch (var e)
println(e.message) // Can not convert '12abc' to a number.
end

texto.toFloat()​

Convierte el texto en un decimal, con todas sus cifras: "3.14" da lo mismo que 3.14. Admite lo mismo que toInt().

var a = "2.5"
var b = "42"
println(a.toFloat() * 2) // 5
println(b.toFloat()) // 42

Si el texto no es un número, no lanza ninguna excepción: devuelve un valor de error, que se imprime como Error 7 (el número de ERR_TYPE_MISMATCH). try no lo captura. Los valores de error se cuentan en Errores y excepciones. Para saber si ha fallado, mira si type() del resultado da tError.

Si el texto puede no ser un número, conviértelo con JSON.parse(texto), del módulo JSON. Lee todas las cifras, aunque algún decimal puede salir distinto en la última, y, si el texto no se puede leer, lanza ERR_INVALID_VALUE, que try sí captura:

use JSON

var texto = "3.14"
try
var x = JSON.parse(texto)
var doble = x * 2
println(doble.toString()) // 6.28
catch (var e)
println("No es un número: ", texto)
end

texto.toBool()​

Devuelve false si la cadena está vacía, y true con cualquier otro texto, también con "false" o "0".

var a = ""
var b = "false"
println(a.toBool()) // false
println(b.toBool()) // true

Para leer un "true" o un "false" escrito en un texto, compáralo:

var texto = "true"
var activo = (texto == "true")
println(activo) // true

Ejemplo completo​

Este programa lee unas puntuaciones escritas como texto, como llegarían de un fichero o de la red. Las limpia, descarta las que no son un número y las imprime ordenadas por nombre.

var datos = "ana:120, LUIS:95 ,  marta:200,pablo:abc"
var tabla = {}

foreach (var entrada in datos.split(","))
var partes = entrada.split(":")
var nombre = partes[0].strip()
var puntos = partes[1].strip()
if (puntos.isDigit()) then
tabla[nombre.lower()] = puntos.toInt()
else
println("No son puntos: ", nombre, " -> ", puntos)
end
end

var nombres = tabla.keys()
nombres.sort()
foreach (var n in nombres)
var linea = n.upper() + ": " + tabla[n].toString()
println(linea)
end
var coma = ", "
println("Jugadores: ", coma.join(nombres))
No son puntos: pablo -> abc
ANA: 120
LUIS: 95
MARTA: 200
Jugadores: ana, luis, marta

Trampas y problemas conocidos​

  • Un método no se puede llamar sobre una cadena escrita entre comillas, ni se puede indexar. "hola".upper() no compila (Expected identifier in method name.), y "hola"[0] compila, pero lanza ERR_TYPE_MISMATCH al ejecutarse. Guarda antes la cadena en una variable. Más en Sintaxis básica.

    var grito = "hola".upper()
  • Una cadena más un número es un error al ejecutar: ver Unir cadenas.

  • < y > no ordenan texto: ver Comparar cadenas.

  • size() cuenta bytes, y upper(), lower() e isAlpha() no conocen las tildes: ver Letras con tilde.

  • "\n" son dos caracteres (CR LF): ver Escribir cadenas y split.

  • toBool() de "false" es true: ver toBool.

  • toFloat() no lanza excepciones: ver toFloat.

  • Fallos de la VM que conviene evitar, cada uno con su aviso: find desde la tercera aparición, rightStrip con una cadena de espacios, replace con una cadena vacía, que se cuelga, y un número asignado a texto[i], que cierra la VM.

Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.