Stringhe

Stringhe

Le stringhe sono sequenze di caratteri. Nei precedenti programmi le stringhe sono state già usate, anche se non erano state introdotte formalmente. La istruzione di stampa System.out.println è infatti una istruzione che stampa una stringa. Per dire all'interprete come è fatta una stringa si può mettere la sequenza di caratteri che la compone fra virgolette doppie. Per esempio, la sequenza di caratteri 'abcd efgs' viene rappresentata come "abcd efgs". Si noti che una stringa può anche contenre degli spazi, per cui "abcd efgs" è una unica stringa. Dal momento che la istruzione System.out.println stampa una stringa, se si esegue System.out.println("abcd efgs") viene stampata la stringa 'abcd efgs'.

Le stringhe sono il primo tipo di dati non numerico che vediamo. Mentre int e double erano rappresentazione di numeri (interi e reali) le stringhe sono sequenze di caratteri. Per il resto, sono tipi di dati come gli altri, per cui si possono definire e usare variabili di tipo stringa. La dichiarazione tipica è la seguente:

String a;

Per esempio, il programma PrimaStringa.java definisce una variabile di tipo stringa, gli assegna una valore (che in questo caso non può essere un numero, ma deve essere una stringa), e la stampa.

/*
  Definisce e usa una variabile di tipo stringa.
*/

class PrimaStringa {
  public static void main(String[] args) {
    String a;

    a="Questa e' una stringa.";

    System.out.println(a);
  }
}

Le espressioni che è possible scrivere con le stringhe sono diverse da quelle sui numeri. Per esempio, l'operatore + nel caso dei numeri rappresenta la normale somma numerica, mentre nel caso delle stringhe è la concatenazione, cioè mette le due stringhe l'una dietro l'altra. Per esempio, la espressione "abcd efg"+"12c" rappresenta la stringa "abcd efg12c", che è la stringa ottenuta mettendo il secondo operando dopo il primo.

Un caso particolare da tenere a mente è che quando si un numero a una stringa, il numero viene prima convertito in stringa, e poi viene fatta la normale concatenazione. Per esempio, la espressione "questo numero vale"+12 dà come risultato la stringa "questo numero vale12". Si faccia attenzione che il numero 12 è molto differente dalla stringa "12". Per esempio, 12+416 come risultato, mentre "12"+4"124". La seconda espressione è infatti la somma di una stringa e di un numero, per cui il numero 4 viene convertito nella stringa "4"; a questo punto si fa la somma fra le due stringhe "12" e "4". Usando la regola della concatenzione, si mette il carattere 4 subito dopo i caratteri 12, per cui si ottiene la stringa 124.

Il programma Concatenazione.java contiene alcuni esempi di operazioni su stringhe.

/*
  Alcuni esempi di concatenazioni fra stringhe.
*/

class Concatenazione {
  public static void main(String[] args) {
    int a=10, b=20, c;
    String z="abcd";
    String x="123";
    String y;

    c=a+b;	// questa e' una somma fra interi: risultato 30

    System.out.println(c);

    y=x+z;	// concatenazione fra stringhe: risultato "123abcd"

    System.out.println(y);

    y=12+"3";	// concatena una stringa a un intero: ris. "123"

    System.out.println(y);

    y=c+x;	// concatena stringa e intero: risultato "30123"

    System.out.println(y);
  }
}

La regola generale è molto semplice: se si somma un intero con una stringa, si ottiene una stringa. Una cosa simile succede sommando un intero a un reale: si ottiene un reale.

Grafico di una funzione in solo testo

Si vuole stampare il grafico della funzione f(x)=|x|/20+20*sin(x/10)+30, usando solo le istruzioni viste fino ad ora. Un metodo semplice per fare questo è usare una riga di testo per ogni valore di x: su ogni riga viene stampata una sequenza di caratteri * lunga quando il valore della funzione f(x).

Supponendo di volere il grafico per x che va da -100 a +100, occorre un ciclo for in cui la x assume valori progressivi da -100 a +100. Ad ogni passo, si calcola il valore di f(x). A questo punto, occorre stampare una stringa composta da tanti asterischi quanto è il valore della funzione.

Il problema è ora come realizzare questa stringa. Il programma GraficoTesto.java risolve questo problema con un ciclo for, in cui si parte da 1 e si arriva al valore della funzione. Ad ogni iterazione si aggiunge un asterisco alla variabile riga. Dal momento che le iterazioni sono tante quanto è il valore della funzione, la stringa alla fine è fatta da tanti * quanto è il valore della funzione.

/*
  Grafico solo testo di una funzione.
*/

class GraficoTesto {
  public static void main(String[] args) {
    double x,f;
    int i;
    String riga;

    for(x=-100; x<=100; x=x+1) {
      f=Math.abs(x)/20+20*Math.sin(x/10)+30;

      riga="";
      for(i=1; i<=f; i=i+1) {
        riga=riga+"*";
      }

      System.out.println(riga);
    }
  }
}

Alcune precisazioni: le funzioni Math.abs e Math.sin calcolano il valore assoluto e il seno di un numero reale. La istruzione riga=""; assegna alla variabile riga la stringa vuota: questa è la sequenza di caratteri che non contiene nessun carattere, ossia la stringa di lunghezza 0. Questa è una stringa valida come tutte le altre. Si può quindi usare come una qualsiasi altra stringa. Quando la stringa vuota viene concatenata a un'altra stringa, si ottiene questa seconda stringa. In termini algebrici, la stringa vuota è l'elemento neutro della operazione di concatenazione fra stringhe.

Il programma GraficoTestoLimitato.java è una variante del precedente in cui si è tenuto conto del fatto che la funzione può superare in valore la larghezza dello schermo. In questo caso, si è scelto di limitare la stringa riga a 60 caratteri. Se la funzione supera questo valore, si aggiunge a riga il carattere "+", e si esce dal ciclo.

/*
  Grafico solo testo di una funzione.
*/

class GraficoTestoLimitato {
  public static void main(String[] args) {
    double x,f;
    int i;
    String riga;

    for(x=-100; x<=100; x=x+1) {
      f=Math.abs(x)/5+20*Math.sin(x/10)+30;

      riga="";
      for(i=1; i<=f; i=i+1) {
        riga=riga+"*";
        if( i>60 ) {
          riga=riga+"+";
          break;
        }
      }

      System.out.println(riga);
    }
  }
}

L'unica differenza rispetto al programma precedente è la presenza della istruzione condizionale in cui si controlla se i ha raggiunto il valore 60. In questo caso, si aggiunge il + alla stringa, e si esce dal ciclo, per cui la stringa risulta fatta di 60 asterischi più il segno di addizione.

Esempio di uso delle stringhe: stampa per righe

Vediamo un possibile uso delle operazioni fra stringhe. Si voglino stampare i numeri della serie di Fibonacci in modo che ogni riga ne contenga cinque invece che uno. Inoltre, ci si vuole fermare alla settima riga.

Il programma Colonne.java ha questa struttura: usiamo un variabile riga che va da 1 a 7. Usiamo poi una seconda variabile colonna che, per ogni riga, va da 1 a 5 (infatti, si vogliono cinque numeri per ogni riga, cioè cinque colonne di numeri). Poi abbiamo ancora le variabili penultimo, ultimo, e prossimo per la serie.

/*
  Serie di Fibonacci, in colonne
*/

class Colonne {
  public static void main(String[] args) {
    int riga, colonna;
    int penultimo, ultimo, prossimo;
    String rigacorrente;

    penultimo=1;
    ultimo=1;

    for(riga=1; riga<=7; riga=riga+1) {
      rigacorrente=" ";

      for(colonna=1; colonna<=5; colonna=colonna+1) {
        rigacorrente=rigacorrente+" "+penultimo;

        prossimo=penultimo+ultimo;
        penultimo=ultimo;
        ultimo=prossimo;
      }

      System.out.println(rigacorrente);
    }
  }
}

Cose da notare in questo programma:

  1. c'è un ciclo for dentro l'altro: questo significa che a ogni passata del ciclo esterno, viene eseguito il ciclo interno; quindi, ogni volta che riga assume un nuovo valore, il ciclo interno viene eseguito per tutti i valori di colonna;
  2. ogni volta che si calcola un nuovo valore della serie, va aggiunto alla variabile di tipo stringa che rappresenta la riga che si sta costruendo; dal momento che rigacorrente+" "+penultimo è la espressione che rappresenta il contenuto della variabile rigacorrente a cui è stato concatenato penultimo, la istruzione rigacorrente=rigacorrente+" "+penultimo; ha come effetto quello di concatenare penultimo a rigacorrente e mettere poi il risultato in rigacorrente;
  3. all'inizio della creazione di una nuova riga, occorre cancellare la riga precedente e ricominciare con i nuovi valori; per questo, all'inizio di ogni riga, si esegue la istruzione rigacorrente=" ";. Come per tutte le variabili, l'assegnazione mette quello che sta a destra nella variabile a sinistra. Questo significa che il contenuto di rigacorrente viene sostituito da " ".