Stampare e Leggere Stringhe in Linguaggio C

Ora che sappiamo cosa sono le stringhe e come definire variabili stringa in Linguaggio C, vediamo come stampare e leggere stringhe da console.

In questa lezione vedremo come usare le funzioni printf e puts per stampare stringhe a schermo e le funzioni scanf e gets per leggere stringhe da console. Vedremo anche come leggere stringhe in modo personalizzato.

Stampare Stringhe con la Funzione printf

Iniziamo a studiare la prima funzione per stampare a schermo stringhe in Linguaggio C: la funzione printf. Questa funzione è definita nell'header di libreria stdio.h.

La printf è una funzione molto potente e flessibile, che permette di stampare a schermo stringhe formattate. Essa vuole, come primo parametro, una stringa che rappresenta la cosiddetta stringa di formato. Vedremo nelle prossime lezioni il dettaglio di come siano fatte le stringhe di formato, anche perché ricorrono in molte altre funzioni.

Per poter stampare una stringa con printf, bisogna usare lo specificatore di conversione %s, che indica che la stringa da stampare è passata come parametro successivo alla stringa di formato.

Ad esempio, il seguente codice prende una variabile stringa, nome, che rappresenta il nome di una persona e stampa a schermo un saluto:

#include <stdio.h>

int main() {
    char nome[] = "Mario";
    printf("Ciao, %s!\n", nome);
    return 0;
}

Il codice stampa a schermo:

Ciao, Mario!

Quando la printf incontra lo specificatore %s, essa prende la stringa corrispondente e la stampa a schermo carattere per carattere, fino a quando non incontra il carattere terminatore '\0'.

Definizione

Stampare Stringhe con printf

La funzione printf è usata per stampare a schermo stringhe formattate. Per stampare una stringa, bisogna usare lo specificatore %s.

printf("%s", stringa);

Nel caso in cui il terminatore dovesse mancare, la printf continuerebbe a stampare a schermo caratteri fino a quando non incontra un terminatore, che potrebbe essere presente in un'altra parte della memoria. In tal caso il comportamento del programma sarebbe indefinito.

Nota

Attenzione alle stringhe passate a printf

Bisogna sempre assicurarsi che le stringhe passate alla funzione printf siano terminate correttamente con il carattere terminatore '\0'.

In caso contrario, il comportamento del programma è indefinito.

Stampare una porzione di una stringa o stringa in un campo

Possiamo anche stampare una porzione di una stringa, anziché tutta la stringa. Per fare ciò, dobbiamo usare lo specificatore %.ps, dove p è un numero intero che rappresenta il numero massimo di caratteri da stampare. Ad esempio, il seguente codice stampa solo i primi tre caratteri della stringa nome:

#include <stdio.h>

int main() {
    char nome[] = "Mario";
    printf("Ciao, %.3s!\n", nome);
    return 0;
}

Il codice stampa a schermo:

Ciao, Mar!

Analogamente, possiamo racchiudere la stringa in un campo, ossia stampare la stringa in un campo vuoto allineandola a destra. Per fare ciò, dobbiamo usare lo specificatore %ms, dove m è un numero intero che rappresenta la larghezza del campo. Ad esempio, il seguente codice stampa la stringa nome allineata a destra in un campo largo 10:

#include <stdio.h>

int main() {
    char nome[] = "Mario";
    printf("Ciao, %10s!\n", nome);
    return 0;
}

Il codice stampa a schermo:

Ciao,      Mario!

In questo caso la stringa "Mario" è più corta di 10 caratteri, quindi viene allineata a destra nel campo. Nel caso in cui la stringa fosse più lunga di 10 caratteri, la stringa sarebbe stampata normalmente.

Possiamo, anche, allineare la stringa a sinistra, usando il segno meno - prima del numero che rappresenta la larghezza del campo. Ad esempio, il seguente codice stampa la stringa nome allineata a sinistra in un campo largo 10:

#include <stdio.h>

int main() {
    char nome[] = "Mario";
    printf("Ciao, %-10s!\n", nome);
    return 0;
}

Il codice stampa a schermo:

Ciao, Mario     !

Lo specificatore di lunghezza, lunghezza campo e allineamento possono essere combinati nella sintassi %-m.ps. Ad esempio, il seguente codice stampa i primi tre caratteri della stringa nome allineati a sinistra in un campo largo 10:

#include <stdio.h>

int main() {
    char nome[] = "Mario";
    printf("Ciao, %-10.3s!\n", nome);
    return 0;
}

Il codice stampa a schermo:

Ciao, Mar       !

Ricapitolando:

Definizione

Dettagli dello specificatore di formato %s

La sintassi generale dello specificatore di formato %s è:

%-m.ps
  • -: allinea la stringa a sinistra nel campo ed è opzionale.
  • m: rappresenta la larghezza del campo. È opzionale.
  • .p: rappresenta il numero massimo di caratteri da stampare. È opzionale.

Stampare Stringhe con la Funzione puts

Esiste un'altra funzione per stampare stringhe in Linguaggio C, la funzione puts. Questa funzione è definita nell'header di libreria stdio.h.

Essa è molto più semplice da utilizzare rispetto alla printf, ma meno flessibile.

Richiede un unico parametro di tipo char *, ossia un puntatore a una stringa, e stampa la stringa a schermo, aggiungendo automaticamente un carattere di nuova linea alla fine.

Ad esempio, il seguente codice stampa a schermo esclusivamente la stringa nome aggiungendo un carattere di nuova linea alla fine:

#include <stdio.h>

int main() {
    char nome[] = "Mario";
    puts(nome);
    return 0;
}

Il codice stampa a schermo:

Mario
Definizione

Stampare Stringhe con puts

La funzione puts è usata per stampare a schermo stringhe. Aggiunge automaticamente un carattere di nuova linea alla fine.

Per usarla, bisogna includere l'header di libreria stdio.h e passare la stringa come parametro.

puts(stringa);

La funzione puts è molto comoda per stampare stringhe, ma non permette di formattare la stringa come fa la printf.

Leggere Stringhe con la Funzione scanf

Vediamo, adesso, come leggere da console una stringa.

La prima funzione di libreria che ci permette di leggere una stringa è la scanf, anch'essa definita nella libreria standard stdio.h.

La scanf vuole, come primo parametro, una stringa di formato così come la printf. Per cui, per poter leggere una stringa possiamo scrivere:

char nome[32];
scanf("%s", nome);

In questo caso, la scanf legge una stringa da console e la memorizza nella variabile nome. Da notare che non abbiamo dovuto usare l'operatore indirizzo & davanti alla variabile nome, in quanto quest'ultima è già un puntatore a caratteri, in quanto stringa.

La scanf, inoltre, aggiunge automaticamente un carattere terminatore '\0' alla fine della stringa letta.

Ad esempio, possiamo scrivere un programma che legge il nome di una persona da console e lo stampa a schermo:

#include <stdio.h>

int main() {
    char nome[32];
    printf("Inserisci il tuo nome: ");
    scanf("%s", nome);
    printf("Ciao, %s!\n", nome);
    return 0;
}

Se proviamo a compilare ed eseguire il programma, possiamo inserire il nostro nome e il programma ci saluterà:

Inserisci il tuo nome: Mario
Ciao, Mario!

Esiste, tuttavia, una limitazione della scanf: essa ignora gli spazi bianchi (spazi e tabulazioni) prima della stringa e si ferma al primo spazio bianco. Questo significa che se inseriamo un nome e cognome, la scanf leggerà solo il nome.

Ad esempio, se inseriamo il nome e cognome Mario Rossi, la scanf leggerà solo Mario:

Inserisci il tuo nome: Mario Rossi
Ciao, Mario!
Definizione

Leggere Stringhe con scanf

La funzione scanf può essere usata per leggere stringhe da console. Per leggere una stringa, bisogna usare lo specificatore %s.

scanf("%s", stringa);

Non è necessario usare l'operatore indirizzo & davanti alla variabile stringa.

La funzione scanf aggiunge automaticamente un carattere terminatore '\0' alla fine della stringa letta.

Quando si usa la scanf ci sono delle limitazioni:

  • Ignora gli spazi bianchi prima della stringa.
  • Si ferma al primo spazio bianco o carattere di nuova linea (invio).

Specificare il numero massimo di caratteri da leggere con scanf

La scanf ha un problema, in quanto non controlla la lunghezza della stringa inserita. Motivo per cui, se inseriamo una stringa più lunga della dimensione del buffer, la scanf sovrascrive la memoria adiacente, causando un comportamento indefinito.

Per ovviare a questo problema possiamo usare lo specificatore di formato %Ns, dove N è un numero intero che rappresenta il numero massimo di caratteri da leggere. Ad esempio, il seguente codice legge una stringa di massimo 31 caratteri:

#include <stdio.h>

int main() {
    char nome[32];
    printf("Inserisci il tuo nome: ");
    scanf("%31s", nome);
    printf("Ciao, %s!\n", nome);
    return 0;
}

In questo caso abbiamo usato lo specificatore %31s, che legge al massimo 31 caratteri, mentre la stringa nome ne può contenere al massimo 32. Questo perché dobbiamo tener conto anche del carattere terminatore '\0'.

Definizione

scanf e numero massimo di caratteri

Per specificare il numero massimo di caratteri da leggere con la scanf, possiamo usare lo specificatore %Ns, dove N è un numero intero.

scanf("%Ns", stringa);

Leggere Stringhe con la Funzione gets

Per superare le limitazioni della scanf, possiamo usare la funzione gets. Questa funzione è definita nell'header di libreria stdio.h.

La funzione gets legge un'intera riga di testo, compresi gli spazi bianchi, fino a quando non incontra un carattere di nuova linea, cioè quando l'utente non preme il tasto invio. Fatto questo, sostituisce il carattere di nuova riga con il terminatore '\0'.

Ad esempio, il programma che segue legge una frase da console e la stampa a schermo:

#include <stdio.h>

int main() {
    char frase[128];

    printf("Inserisci una frase: ");
    gets(frase);

    printf("Hai inserito: %s\n", frase);
    return 0;
}

Se proviamo a compilare ed eseguire il programma, possiamo inserire una frase e il programma la stamperà a schermo:

Inserisci una frase: Questa è una frase di esempio.
Hai inserito: Questa è una frase di esempio.
Definizione

Leggere Stringhe con gets

La funzione gets può essere usata per leggere un'intera riga di testo da console, compresi gli spazi bianchi.

gets(stringa);

La funzione gets sostituisce il carattere di nuova riga con il terminatore '\0'.

La funzione gets è molto comoda per leggere intere righe di testo da console, ma ha un problema di sicurezza: non controlla la lunghezza della stringa che legge. Questo significa che se l'utente inserisce una stringa più lunga della dimensione del buffer, la funzione sovrascrive la memoria adiacente, causando un comportamento indefinito.

Esistono, come vedremo, funzioni più sicure per leggere stringhe da console, come la fgets.

Nota

Attenzione alla funzione gets

La funzione gets non controlla la lunghezza della stringa che legge.

Leggere Stringhe in modo personalizzato

Le funzioni scanf e gets che abbiamo visto sopra sono poco flessibili e hanno problemi intrinseci di sicurezza. Motivo per cui, molti programmatori preferiscono scrivere le proprie funzioni per leggere stringhe da console.

In questa sezione proveremo, anche per scopi didattici, a scrivere una funzione personalizzata per leggere stringhe da console.

Nel progettare una funzione di questo tipo dobbiamo considerare alcuni aspetti:

  1. Se la funzione deve scartare o meno gli spazi bianchi iniziali;
  2. Se la funzione deve leggere una riga intera o solo una parola;
  3. Se la funzione deve controllare la lunghezza della stringa;
  4. Quando la funzione deve terminare la lettura. Ad esempio, se deve terminare la lettura al primo spazio bianco o al primo carattere di nuova linea.

Detto questo, supponiamo di voler realizzare una funzione leggi_linea che legge una riga intera da console e si ferma al primo carattere di nuova linea.

Inoltre, la funzione controlla la lunghezza massima della stringa e restituisce il numero di caratteri letti.

Il prototipo di questa funzione potrebbe essere:

int leggi_linea(char *stringa, int lunghezza);

Dove stringa è il puntatore alla stringa in cui memorizzare i caratteri letti e lunghezza è la lunghezza massima della stringa. La funzione restituisce il numero di caratteri letti. Se la linea di testo è più lunga della lunghezza massima, la funzione scarta i caratteri in eccesso.

Per implementare questa funzione possiamo leggere carattere per carattere da console e memorizzarli nella stringa finché non incontriamo un carattere di nuova linea o raggiungiamo la lunghezza massima. Per far questo, usiamo la funzione getchar che legge un singolo carattere da console.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int leggi_linea(char *stringa, int lunghezza) {
    int c;
    int i = 0;

    while ((c = getchar()) != '\n' &&
           (c != EOF) &&
           (i < lunghezza - 1)) {
        stringa[i++] = c;
    }

    stringa[i] = '\0';

    return i;
}

Il codice funziona nel seguente modo:

  1. Riga 3: Inizializziamo un contatore i a zero;
  2. Righe 5-7: Leggiamo un carattere da console finché non incontriamo un carattere di nuova linea, un carattere EOF (End of File) o non raggiungiamo la lunghezza massima meno 1;
  3. Riga 8: Memorizziamo il carattere letto nella stringa e incrementiamo il contatore i;
  4. Riga 11: Terminiamo la stringa con il carattere terminatore '\0';
  5. Riga 13: Restituiamo il numero di caratteri letti.

Da notare che il tipo del carattere letto, ch, è di tipo int e non char. Questo perché la funzione getchar restituisce un intero. Se fosse un char il confronto con EOF non sarebbe possibile.

Nella funziona abbiamo anche aggiunto il controllo del numero di caratteri letti, tenendo conto anche del carattere terminatore '\0'.

Possiamo usare la funzione leggi_linea nel seguente programma:

#include <stdio.h>

int leggi_linea(char *stringa, int lunghezza);

int main() {
    char frase[128];

    printf("Inserisci una frase: ");
    leggi_linea(frase, 128);

    printf("Hai inserito: %s\n", frase);
    return 0;
}

int leggi_linea(char *stringa, int lunghezza) {
    int c;
    int i = 0;

    while ((c = getchar()) != '\n' &&
           (c != EOF) &&
           (i < lunghezza - 1)) {
        stringa[i++] = c;
    }

    stringa[i] = '\0';

    return i;
}

In Conclusione

In questa lezione abbiamo visto come stampare e leggere stringhe in Linguaggio C:

  • Abbiamo imparato a stampare stringhe con la funzione printf usando lo specificatore %s;
  • Abbiamo visto come stampare una porzione di una stringa o allinearla in un campo;
  • Abbiamo imparato a stampare stringhe con la funzione puts;
  • Abbiamo imparato a leggere stringhe con la funzione scanf e gets;
  • Abbiamo visto come leggere stringhe in modo personalizzato.

Nella prossima lezione vedremo come accedere ai singoli caratteri di una stringa in Linguaggio C.