Strings

Objetivos: Mostrar cómo se manipulan las cadenas de caracteres en Java.

Temas:


El tipo String

Así como en Java se pueden almacenar enteros en variables de tipo int o números reales en variables de tipo double, también se pueden almacenar secuencias de caracteres en variables de tipo String. ¡Cuidado! El tipo se escribe con la S mayúscula.

Un string es una secuencia de letras (a-z o A-Z), dígitos (0-9) o símbolos (% ^ & @ [ ] etc.).

Constantes:

En un programa se pueden escribir strings constantes encerrando los caracteres entre " ... ":

    "x ? "
    "Promedio= "
    "a"
    "123"
¡Cuidado! En un programa la constante 5 denota el entero 5, mientras que la constante "5" denota un string y por lo tanto no se puede operar aritméticamente.

Variables:

    String nombre; // sin inicialización
    String direccion= "Alameda 123";
    int edad;
Los valores que pueden almacenar las variables nombre y dirección deben ser strings, mientras que edad sólo puede almacenar enteros.

Asignación:

    nombre= "Juan Gonzalez";
    edad= 18;
    direccion= 123; // ¡Error!
    edad= "18";     // ¡Error!

Operaciones sobre strings

Lectura de un string desde el teclado:

    nombre= readString();
    direccion= readLine();
La función readString() lee una sola palabra (hasta encontrar un espacio en blanco o un cambio de línea). La función readLine lee hasta encontrar el cambio de línea. Los espacios forman parte del string leído. Por ejemplo, si el usuario ingresa:

    juan alameda 123
la variable nombre quedará con el string "juan" y dirección con "alameda 123".

Despliegue en pantalla:

    println(nombre);
    print(direccion);
    println("x");
desplegará:

    juan
    alameda 123x
En general, las funciones print y println despliegan expresiones de tipo String. En particular pueden desplegar una variable o una constante.

Largo de un string:

    int largo= length(nombre);
La función length entrega un entero que corresponde al número de caracteres contenidos en el string. Por ejemplo:

    length("abc") es 3
    length("a b c") es 5
    length("1 2 3 ") es 6
    length(123) es un error porque 123 no es un string
Observación: la constante "" denota el string vacío cuyo largo es 0.

Comparación:

Se puede comparar lexicográficamente dos strings. Diremos que "juan" es menor lexicográficamente que "pedro", porque aparece antes en el diccionario.

    if (compare(nombre1, nombre2)<0) {
      print("menor");
    }
    else if (compare(nombre1, nombre2)==0) {
      print("iguales");
    }
La función compare retorna un valor <0 si el primer argumento es menor lexicográficamente que el segundo argumento, 0 si son iguales o >0 si el primero es mayor.


Ejercicio 1:

Escriba un programa que lea palabras del teclado e indique el largo de cada una de ellas. El fin de las palabras está marcado por la palabra "fin". El diálogo debe ser:

Diálogo:

esta noche puedo escribir los versos más tristes fin
largo de esta: 4
largo de noche: 5
...
largo de tristes: 7

Ejercicio 2:

Escribir un programa que lea una cantidad indeterminada de palabras (terminada en la palabra "fin") y escriba la palabra más larga y la mayor en orden lexicográfico.

Diálogo:

esta noche puedo escribir los versos más tristes fin
Para la frase anterior la palabra más larga es escribir y la mayor es versos. Solución del problema:

    print("? ");
    String palabra= readString();
    String mayor= palabra;
    String maslarga= palabra;
    while (compare(palabra, "fin")!=0) {
      if (length(palabra)>length(maslarga)) {
        maslarga= palabra;
      }
      if (compare(palabra,mayor)>0) {
        mayor= palabra;
      }
      palabra= readString();
    }
    print("Mayor= "); println(mayor);
    print("Mas larga= "); println(maslarga);
(Ver el programa completo en MasLarga.java.)

Observe la similitud de este programa con el programa que calcula el máximo y mínimo de un conjunto de números ingresados por el usuario (visto en una clase anterior).


Concatenación: +

Los strings se pueden concatenar con el operador +:

    "hola" + "juan"   es   "holajuan"
    "hola" + " " + "juan" es ("hola" + " ") + "juan" o "hola juan"
Por lo tanto, las dos últimas líneas se pueden reescribir como:

    println("Mayor= "+mayor);
    println("Mas larga= "+maslarga);
También se puede escribir cuán larga era una palabra:

    println("El largo de "+maslarga+" es "+length(maslarga));
Al hacer la parentización queda al último una expresión de tipo string + una expresión entera. En este caso Java convierte la expresión entera al string que representa el valor de la expresión y luego realiza la concatenación. Por ejemplo:

    "numero= "+5    es   "numero= 5"
    ""+123          es   "123"
De ahora en adelante, cuando queramos desplegar un string seguido de una valor numérico, preferiremos escribir en forma abreviada:

    println("El numéro es "+num);
    println("El rango es ["+min+","+max+"]");
Por lo tanto debe tener mucho cuidado. A veces el operador + significa suma (de enteros o de reales) y a veces concatenación de strings. Es un error usar los operadores -, * o / con strings.


Extracción de substrings

Cada uno de los caracteres que forman un string tiene una posición en él. Por ejemplo se tiene el siguiente string:

    String s=         "abracadabra";
    las posiciones son 0123456789 
                               y 10
Es decir que el caracter de la posición 0 es una a, el de la posición 4 es una c y el de la posición 10 es una a. El largo del string en este caso es 11. No es válido hablar del caracter que está en la posición 11 o en la -1.

Se puede obtener un substring a partir de otro string usando la función substring. Por ejemplo:

    String subs= substring(s,4,3);
La función substring recibe como argumentos un string, del cual se obtendrá un substring, la posición del primer caracter que formará parte del substring y el número de caracteres que debe contener el substring.

El resultado de esta operación es que subs es "cad". Es importante notar que el substring entregado es también un String. La posición de la c es 0, la posición de la a es 1 y la posición de la d es 2. El largo del substring es 3.

También existe:

    String resto= substring(s,4); // resto== "cadabra"
Esta forma de llamar a substring obtiene el substring de s que parte en la posición 4 y llega hasta el final de s.

Ejercicio 3:

Reemplazar las letras a por e en el string s, dejando el resultado en t. Por ejemplo si s contiene "abracadabra", t deberá quedar como "ebrecedebre". Solución: Estudiar el siguiente programa.

    String t= "";
    int i= 0;
    while (i<length(s)) {
      t= t+substring(s,i,1);
      i= i+1;
    }
Este programa es una nueva forma de acumulación. Antes de entrar al ciclo, el string t está vacío. En la primera iteración contiene el primer caracter de s ("a"). En la segunta, el primero y el segundo ("ab") y en cada iteración se agrega un nuevo caracter, hasta que al final t y s son el mismo string.

Usamos el mismo patrón, solo que ahora antes de agregar un caracter nos fijamos si es una "a", en cuyo caso agregamo una "e" y no la "a":

    String t= "";
    int i= 0;
    while (i<length(s)) {
      if (compare(substring(s,i,1), "a")==0)
        t= t+"e";
      else
        t= t+substring(s,i,1);
      i= i+1;
    }
Esa es la característica de los patrones. Todas sus formas se parecen, pero siempre son diferentes de alguna forma.

Esto mismo se puede hacer más cómodamente con la función replace que reemplaza todas las ocurrencias de un substring dentro de un string mayor por otro substring. El ejercicio quedaría simplemente como:

    String t= replace(s,"a","e");

Ejercicio 4:

Invertir los caracteres del string s, dejando el resultado en s. Por ejemplo si s contiene "roma", entonces t deberá quedar en "amor".

Indicación: cambie ligeramente el patrón de acumulación, de modo que en la segunda iteración t sea "ba" en vez de "ab". En la tercera, "rba", en la cuarta "arba", etc.

Solución:

Invertir los caracteres del string s, dejando el resultado en s. Por ejemplo si s contiene "roma", entonces t deberá quedar en "amor".

En la clase anterior, utilizamos la instrucción:

    t= t+substring(s,i,1);
Esta instrucción agrega un caracter a t por la derecha. Ahora, lo que se necesita es agregar caracteres a t por la izquierda:

    String t= "";
    int i= 0;
    while (i<length(s)) {
      t= substring(s,i,1) + t; // (*)
      i= i+1; // (**)
    }
La siguiente tabla muestra los valores que toman las variables i y t, justo después de ejecutar la instrucción marcada con (*), para el caso en que s es "roma".

Lugar Valor de i Valor de t
1era. iteración 0 "r"
2da. iteración 1 "or"
3era. iteración 2 "mor"
4ta. iteración 3 "amor"

Y no hay una quinta iteración porque en la 4ta. iteración, después de ejecutar (**), i toma el valor 4 que no cumple con la condición del while.


Ubicar un substring dentro de otro: indexOf

Para determinar si un string es parte de otro substring se usa la función indexOf:

    indexOf("te dije hola", "dije")   es 3
    indexOf("te dije hola", "te")     es 0
    indexOf("te dije hola", "hola")   es 8
    indexOf("te dije hola", "Hola")   es -1
    indexOf("te dije hola", "hola ")  es -1
Esta función recibe 2 argumentos y determina si el segundo es un substring del primero. Si el valor retornado es -1, el segundo argumento no es un substring del primero. Si es mayor o igual a 0, es la posición del primer caracter del substring en el string mayor.

También se puede especificar a partir de qué posición se realiza la búsqueda:

    indexOf("te dije hola", "dije", 4)   es -1
    indexOf("te dije hola", "hola", 4)   es 8
    indexOf("te dije hola", "hola", 100) es -1
Ejercicio 5:

Haga un programa que indique cuantas veces aparece un string dentro de otro string mayor. El diálogo debe ser:

    Frase ? esta noche puedo escribir los versos más tristes
    String ? es
    es aparece 3 veces
Solución:

    print("? ");
    String frase= readLine();
    print("? ");
    String substr= readLine();
    int cont= 0;
    int posicion= indexOf(frase, substr);
    while (posicion!=-1) {
      cont= cont+1;
      frase= substring(frase, posicion+length(substr)); // (*)
      posicion= indexOf(frase, substr);
    }
    println(substr+" aparece "+cont+" veces");
(Ver el programa completo en StringFrec.java.)

Observe que en la instrucción remarcada, se elimina de la frase tanto la parte que no correspondía al substring, como también el substring. La siguiente tabla muestra los valores que toman las variables frase, posición y cont, justo después de ejecutar (*) (con frase= "abracadabra" y substr="br"):

Lugar frase posición cont
1era. iteración "abracadabra" 1 1
2da. iteración "acadabra" 5 2
Al salir del ciclo "a" -1 2

La siguiente solución no modifica la variable frase. Utiliza la variante de indexOf en la que se indica a partir de donde se realiza la búsqueda:

    ...
    int posicion= indexOf(frase, substr);
    while (posicion!=-1) {
      cont= cont+1;
      posicion= indexOf(frase, substr, posicion+length(substr));
    }
    println(substr+" aparece "+cont+" veces");

Supresión de espacios no significativos: trim

La función trim permite eliminar los espacios en blanco que aparezcan al principio y al final de un string:

    String s= "   Hola como   estas  ";
    String t= trim(s); // "Hola como   estas";
Sólo elimina los espacios sobrantes al principio y al final, no los que separan palabras.

Ejercicio 6:

Contar las palabras que hay en una línea (separadas por un espacio):

    Frase ? esta noche   puedo escribir los versos más tristes
    Se leyeron 8 palabras
Observación: En este caso, todas las palabras vienen en la misma línea, y por lo tanto no se indica el final con la palabra "fin". Además dos palabras pueden venir separadas por varios espacios en blanco.

Solución: eliminar los espacios superfluos y buscar el primer espacio en blanco. Luego eliminar los espacios superfluos y buscar un nuevo espacio en blanco, etc.

Solución:

    print("? ");
    String frase= readLine();
    int cont= 0;
    frase= trim(frase); // (*)
    int posicion= indexOf(frase, " ");
    while (posicion!=-1) {
      cont= cont+1;
      frase= trim(substring(frase, posicion+1)); // (**)
      posicion= indexOf(frase, " "); 
    }
    if (compare(frase,"")!=0) cont= cont+1; // (***)
    println("Se leyeron "+cont+" palabras");
(Ver el programa completo en Cuenta.java.)

La instrucción (*) es necesaria porque la línea leída puede contener espacios al comienzo.

En (**) se elimina la primera palabra de la frase, luego se eliminan los espacios superfluos y finalmente se asigna el resultado a frase para que continúe el ciclo.

Observe que se realizan las dos operaciones en una sola instrucción, sin tener que pasar por una variable intermedia. También se pudo haber escrito directamente:

    print("? ");
    String frase= trim(readLine());
    ...
A la salida del ciclo en (***), la frase puede ser ya sea "" o bien una nueva palabra (sin espacios). El primer caso no constituye una nueva palabra, pero el segundo sí, por lo que hay que sumar uno a cont.


Ejercicio 7:

Complete la siguiente tabla con los valores de las variables después de ejecutar (**):

Lugar frase posición cont
Justo antes del ciclo "esta noche puedo escribir" 4 0
1era. iteración ? ? ?
...

Modifique el programa anterior de modo que se despliegue la palabra más corta y la mayor léxicográficamente.