[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home] [volume] [parte]


Capitolo 585.   C: conversione di input e output

Il linguaggio C rappresenta in memoria i valori numerici in modo binario secondo una modalità diversa rispetto a quella usata per le stringhe che servono invece per l'interazione umana. In altri termini, un conto è il valore 100, un altro è la sequenza dei caratteri numerici con cui questo valore viene rappresentato sullo schermo o su carta.

Il linguaggio C non svolge automaticamente conversioni da valori numerici binari a stringhe di cifre numeriche e viceversa; per questo è necessario invece avvalersi di funzioni di conversione. Per la precisione esistono due gruppi di funzioni, ...printf() e ...scanf(), con cui è possibile comporre (nel senso tipografico) le informazioni in uscita, oppure interpretarle in senso inverso le informazioni in ingresso.

585.1   Composizione dell'output

Le funzioni del gruppo ...printf() consentono di comporre una stringa (da memorizzare o da visualizzare), partendo da un'altra stringa contenente il formato di composizione e utilizzando un elenco variabile di argomenti:

...printf ( ... stringa_di_composizione[, argomento]... )

Il modello sintattico dà solo una visione di massima: a seconda della funzione ci possono essere dei parametri che non vengono chiariti nello schema, quindi appare sempre la stringa di composizione, la quale può essere seguita da altri argomenti le cui caratteristiche non sono precisate nel prototipo della funzione.(1)

La stringa di composizione è una stringa normale, in cui si inseriscono delle sequenze precedute dal simbolo %, note come specificatori di conversione. Conviene partire da un esempio, proprio con la funzione printf(), la quale emette la stringa generata dalla composizione attraverso lo standard output (attraverso il flusso di file associato allo standard output):

...
printf ("Il capitale di %d al tasso %f%% dà l'interesse %d", 1000, 0.5, 5);
...

Da questa istruzione si ottiene la visualizzazione della frase seguente:

Il capitale di 1000 al tasso 0.500000% dà l'interesse 5

In pratica, al posto del primo specificatore %d è stato inserito il valore 1 000 dopo averlo convertito in modo da essere rappresentato da quattro caratteri ('1', '0', '0', '0'), al posto del secondo specificatore %f è stato inserito il valore 0.5 dopo un'opportuna conversione in caratteri, al posto del terzo specificatore %% è stato inserito un carattere di percentuale, infine, al posto del quarto specificatore %d è stato inserito il valore 5.

Figura 585.3. Schematizzazione della trasformazione di una stringa di composizione in una stringa finale.

composizione dell'output

Lo specificatore di conversione ha due compiti: indicare che tipo di informazione viene prelevato dagli argomenti (ammesso che si prelevi effettivamente un valore) e come questa deve essere rappresentata. Nel caso dell'esempio, il primo specificatore %d indica che il valore da prelevare dagli argomenti è di tipo int; il secondo specificatore %f indica un tipo double; il terzo non preleva alcun valore; il quarto indica ancora un altro int.

Una stringa di composizione che non contenga degli specificatori rimane evidentemente intatta e non richiede alcun dato aggiuntivo. La funzione printf() (che è stata usata nell'esempio) viene usata spesso come mezzo generico per emettere un messaggio attraverso lo standard output, anche quando non c'è alcun bisogno di comporre dei dati. Questo è lecito, ma non va dimenticato il contesto, pertanto, scrivere l'istruzione seguente sarebbe sbagliato:

:-(

...
printf ("Il capitale di 1000 al tasso 0.5% dà l'interesse 5");
...

Il modo giusto è quello seguente:

:-)

...
printf ("Il capitale di 1000 al tasso 0.5%% dà l'interesse 5");
...

585.2   Rappresentazione degli specificatori di composizione per l'emissione dei dati

Di norma, la scelta dello specificatore determina il tipo di dati dell'argomento e il tipo di trasformazione che deve ricevere. La tabella 585.6 elenca alcuni degli specificatori di conversione utilizzabili, nella loro forma più semplice. È bene ricordare che per rappresentare il simbolo di percentuale si usa uno specificatore fittizio composto dalla sequenza di due segni percentuali: %%.

Tabella 585.6. Alcuni specificatori di conversione.

Simbolo Corrispondenza
%...c
Un carattere singolo.
%...s
Una stringa.
%...d
Un intero con segno da rappresentare in base dieci.
%...u
Un intero senza segno da rappresentare in base dieci.
%...o
Un intero senza segno da rappresentare in ottale.
%...x
Un intero senza segno da rappresentare in esadecimale.
%...e
Un numero a virgola mobile normale (double), da rappresentare in notazione esponenziale.
%...f
Un numero a virgola mobile normale (double), da rappresentare in notazione decimale fissa.

Leggendo la tabella si può osservare che la composizione dei dati in uscita può riguardare anche dati che sono già in forma di stringa (lo specificatore %...s), pertanto si usa questo metodo anche per il concatenamento delle stringhe.

Gli specificatori di conversione possono contenere indicazioni ulteriori tra il simbolo di percentuale e la lettera che definisce il tipo di trasformazione. Si tratta di inserire un simbolo composto da un carattere singolo, seguito eventualmente da altre informazioni aggiuntive, secondo la sintassi seguente:

%[simbolo][n_ampiezza][.n_precisione][hh|h|l|ll|j|z|t|L]tipo

Alcuni di questi simboli sono rappresentati dalla tabella 585.7. In presenza di valori numerici, si può indicare il numero di cifre decimali intere (ampiezza), aggiungendo eventualmente il numero di decimali (precisione), se si tratta di rappresentare un numero a virgola mobile. Quando è necessario modificare il tipo di dati provenienti dagli argomenti, ciò può essere precisato con una sigla, come descritto nella tabella 585.8.

Tabella 585.7. Alcuni simboli per la conversione di valori numerici.

Simbolo Corrispondenza
%+...
Il segno «+», usato all'inizio di uno specificatore di conversione, fa sì che i numeri positivi siano rappresentati con il segno in modo esplicito, mentre altrimenti il segno viene mostrato solo per quelli negativi.
%0ampiezza...
%+0ampiezza...
Lo zero, usato all'inizio di uno specificatore di conversione, fa sì che si inseriscano degli zeri per allineare a destra un valore numerico, nell'ambito dell'ampiezza specificata. Lo zero può combinarsi con il segno «+».
%ampiezza...
%+ampiezza...
In mancanza di uno zero iniziale, in presenza dell'indicazione dell'ampiezza, il valore viene allineato a destra usando degli spazi.
%-...
%-+...
Il segno meno richiede un allineamento a sinistra rispetto al campo, usando degli spazi a destra. Si può combinare con il segno «+», ma non con lo zero.

Tabella 585.8. Alcuni modificatori dell'estensione che ha in memoria il valore da estrarre e comporre.

Simbolo Corrispondenza
%...ld
%...lu
%...lo
%...lx
La lettera l, prima di quella che specifica il tipo di composizione, quando si tratta della conversione di un numero intero, specifica che il dato va trattato in qualità di long int, con o senza segno.
%...lc
%...ls
La lettera l, prima di quella che specifica il tipo di composizione, quando si tratta della conversione di un carattere o di una stringa, specifica che il dato va trattato in qualità di carattere esteso (wide char) o di stringa estesa (wide string).
%...lld
%...llu
%...llo
%...llx
La coppia di lettere ll, prima di quella che specifica il tipo di composizione, quando si tratta della conversione di un numero intero, specifica che il dato va trattato in qualità di long long int, con o senza segno.
%...Le
%...Lf
La lettera L, prima di quella che specifica il tipo di composizione, quando si tratta della conversione di un valore in virgola mobile, specifica che il dato va trattato in qualità di long double.

Nella stringa di composizione possono apparire anche sequenze di escape come già mostrato nella tabella 573.17. Si veda anche la pagina di manuale printf(3).

Tabella 585.9. Esempi di utilizzo degli specificatori di conversione di printf(). Le costanti numeriche utilizzate negli esempi sono interpretate secondo le convenzioni del linguaggio, pertanto: 123 e -123 sono costanti di tipo int; mentre 123.456 e -123.456 sono costanti di tipo double.

Codice Risultato emesso attraverso la funzione
printf ("[%d]", 123);
[123]
printf ("[%d]", -123);
[-123]
printf ("[%2d]", 123);
[123]

L'indicatore %2d specifica che si devono usare almeno due cifre, ma se le cifre della parte intera sono in numero maggiore, queste vanno indicate tutte ugualmente.

printf ("[%6d]", 123);
[   123]
printf ("[%6d]", -123);
[  -123]
printf ("[%+6d]", 123);
[  +123]
printf ("[%06d]", 123);
[000123]
printf ("[%06d]", -123);
[-00123]
printf ("[%+06d]", 123);
[+00123]
printf ("[%-6d]", 123);
[123   ]
printf ("[%u]", 123);
[123]
printf ("[%u]", -123);
[4294967173]

Evidentemente si ottiene la rappresentazione del valore binario, tale e quale, secondo la notazione usata per i valori negativi.

printf ("[%6x]", 123);
[    7b]
printf ("[%06x]", 123);
[00007b]
printf ("[%x]", 123);
[7b]
printf ("[%x]", -123);
[ffffff85]

Evidentemente si ottiene la rappresentazione del valore binario, tale e quale, secondo la notazione usata per i valori negativi.

printf ("[%6x]", 123);
[    7b]
printf ("[%06x]", 123);
[00007b]
printf ("[%o]", 123);
[173]
printf ("[%o]", -123);
[37777777605]

Evidentemente si ottiene la rappresentazione del valore binario, tale e quale, secondo la notazione usata per i valori negativi.

printf ("[%6o]", 123);
[   173]
printf ("[%06o]", 123);
[000173]
printf ("[%f]", 123.456);
[123.456000]
printf ("[%f]", -123.456);
[-123.456000]
printf ("[%12f]", 123.456);
[  123.456000]
printf ("[%.4f]", 123.456);
[123.4560]
printf ("[%12.4f]", 123.456);
[    123.4560]
printf ("[%12.4f]", -123.456);
[   -123.4560]
printf ("[%+12.4f]", 123.456);
[   +123.4560]
printf ("[%012.4f]", 123.456);
[0000123.4560]
printf ("[%012.4f]", -123.456);
[-000123.4560]
printf ("[%+012.4f]", 123.456);
[+000123.4560]
printf ("[%-12.4f]", 123.456);
[123.4560    ]
printf ("[%e]", 123.456);
[1.234560e+02]
printf ("[%e]", -123.456);
[-1.234560e+02]
printf ("[%15e]", 123.456);
[   1.234560e+02]
printf ("[%.4e]", 123.456);
[1.2346e+02]
printf ("[%15.4e]", 123.456);
[     1.2346e+02]
printf ("[%15.4e]", -123.456);
[    -1.2346e+02]
printf ("[%+15.4e]", 123.456);
[    +1.2346e+02]
printf ("[%015.4e]", 123.456);
[000001.2346e+02]
printf ("[%015.4e]", -123.456);
[-00001.2346e+02]
printf ("[%+015.4e]", 123.456);
[+00001.2346e+02]
printf ("[%-15.4e]", 123.456);
[1.2346e+02     ]
printf ("[%s]", "ciao amore");
[ciao amore]
printf ("[%7s]", "ciao amore");
[ciao amore]

La stringa è più lunga di sette caratteri, ma viene visualizzata completamente.

printf ("[%.7s]", "ciao amore");
[ciao am]

La stringa viene troncata se è più lunga del valore della precisione.

printf ("[%.14s]", "ciao amore");
[ciao amore]
printf ("[%14s]", "ciao amore");
[    ciao amore]
printf ("[%14.7s]", "ciao amore");
[       ciao am]
printf ("[%-14s]", "ciao amore");
[ciao amore    ]
printf ("[%-14.7s]", "ciao amore");
[ciao am       ]

585.3   Funzioni per la composizione dell'output

Tutte le funzioni standard il cui nome finisce per printf interpretano una stringa di composizione secondo le modalità descritte nel capitolo, ovvero in modo analogo a printf() che, in particolare, emette il risultato della composizione attraverso lo standard output. In particolare, la funzione fprintf() scrive il risultato attraverso il flusso di file che costituisce il parametro stream (il primo argomento) e la funzione sprintf() copia il risultato, come stringa, a partire dal puntatore s (sempre il primo argomento).

int printf (const char *restrict composizione, ...);
int fprintf (FILE *restrict stream, const char *restrict composizione, ...);
int sprintf (char *restrict s, const char *restrict composizione, ...);
int snprintf (char *restrict s, size_t n, const char *restrict composizione, ...);

Le funzioni di cui è appena stato mostrato il modello sintattico, leggono gli argomenti successivi alla stringa di composizione in base a quanto indicato con gli specificatori di composizione. Altre funzioni equivalenti, con il nome che inizia con la lettera «v», hanno bisogno di un puntatore di tipo va_list:

int vprintf (const char *restrict composizione, va_list arg);
int vfprintf (FILE *restrict stream, const char *restrict composizione, va_list arg);
int vsprintf (char *restrict s, const char *restrict composizione, va_list arg);
int vsnprintf (char *restrict s, size_t n, const char *restrict composizione, va_list arg);

585.4   Concatenamento di stringhe

Il linguaggio C, di per sé, non agevola l'uso delle stringhe; al massimo si può contare sul fatto che una sequenza di stringhe letterali venga considerata una stringa sola, concatenata. Per il concatenamento delle stringhe sono disponibili le funzioni strcat() e strncat(), ma l'uso delle funzioni previste per la composizione dell'output è molto più comodo, considerata la facilità con cui si inseriscono anche dati diversi dalle stringhe.

#include <stdio.h>
int main (void)
{
    char s[] = "Ciao amore";
    printf ("%s... Ti %s.\n", s, "voglio tanto bene");
    return 0;
}

L'esempio appena mostrato dovrebbe dimostrare questa maggiore facilità. Il messaggio che viene visualizzato è: «Ciao amore... Ti voglio tanto bene.»

585.5   Interpretazione dell'input

Quando un programma interagisce con l'essere umano, scambia dati in forma grafica, nel senso che un numero appare e viene inserito come sequenza di caratteri grafici. Così come per la rappresentazione umana dei dati si usano comunemente le funzioni ...printf(), per l'immissione dei dati si usano le funzioni ...scanf() che hanno il ruolo opposto:

...scanf ( ... stringa_di_conversione[, argomento]... )

Il modello sintattico dà solo una visione di massima: a seconda della funzione ci possono essere dei parametri che non vengono chiariti nello schema, quindi appare sempre la stringa di conversione, la quale può essere seguita da altri argomenti costituiti da puntatori, le cui caratteristiche particolari non sono precisate nel prototipo della funzione.(2) Viene proposto un esempio con la funzione scanf() che riceve i dati in ingresso (da interpretare) dallo standard input:

...
printf ("Inserisci l'importo: ");
scanf ("%d", &i_importo);
...

Il pezzo di codice mostrato emette la frase seguente e resta in attesa dell'inserimento di un valore numerico intero, seguito da [Invio]:

Inserisci l'importo: _

Questo valore viene inserito nella variabile i_importo. Si deve osservare il fatto che i parametri successivi alla stringa di conversione sono dei puntatori, per cui, avendo voluto inserire il dato nella variabile i_importo, questa è stata indicata preceduta dall'operatore & in modo da fornire alla funzione l'indirizzo corrispondente (si veda il capitolo 577 sulla gestione dei puntatori).

Con una stessa funzione di questo tipo è possibile inserire dati per diverse variabili, come si può osservare dall'esempio seguente, ma in questo caso, per ogni dato viene richiesta la pressione di [Invio] o l'inserimento di spazi tra un dato e l'altro.

...
printf ("Inserisci il capitale e il tasso: ");
scanf ("%d%f", &i_capitale, &i_tasso);
...

La stringa di conversione è il parametro più delicato di queste funzioni. Come visto negli esempi, una stringa del genere contiene principalmente degli specificatori di conversione che, come già accennato, si comportano in modo molto simile agli specificatori di composizione delle funzioni ...printf(). Quello che segue è lo schema sintattico generale per la definizione di uno specificatore di conversione:

%[*][n_ampiezza][hh|h|l|ll|j|z|t|L]tipo

Come si può vedere, all'inizio è previsto un solo tipo di simbolo, costituito da un asterisco, il cui scopo è quello di annullare l'assegnamento del valore a una variabile. In pratica, con l'asterisco il dato corrispondente allo specificatore viene interpretato, ma poi non viene salvato in alcuna variabile.

Successivamente può apparire un numero che rappresenta l'ampiezza del dato da interpretare, in byte, il cui scopo è quello di limitare la lettura fino a un certo carattere (inteso come char, pertanto le sequenze multibyte contano per più di una unità singola). In questo caso non esiste la possibilità di indicare una precisione.

Dopo può apparire una sigla, composta da una o più lettere, il cui scopo è quello di modificare la dimensione predefinita della variabile di destinazione. In altri termini, senza questo modificatore si intende che la variabile ricevente debba essere di una certa grandezza, ma con l'aggiunta del «modificatore di lunghezza» si precisa invece qualcosa di diverso. In pratica, il modificatore di lunghezza usato da queste funzioni è equivalente a quello delle funzioni di composizione dell'output.

Al termine dello specificatore di conversione appare una lettera che dichiara come deve essere interpretato il dato in ingresso e, in mancanza del modificatore di lunghezza, indica anche la dimensione predefinita della variabile ricevente.

Secondo la documentazione standard, il contenuto delle stringhe di conversione si suddivide in «direttive» che, in linea di massima, dovrebbero comporsi secondo il modello seguente:

[spazi]carattere_multibyte|%...

Pertanto, una direttiva può contenere degli spazi, un carattere (inteso in senso tipografico e quindi può occupare più di un byte) oppure uno specificatore di conversione. Visto da un altro punto di vista, la stringa di conversione è composta principalmente da specificatori di conversione che però possono essere alternati da spazi o altri caratteri: gli spazi indicano che in quella posizione possono esserci spazi che vengono ignorati; altri caratteri devono invece corrispondere esattamente nell'input e vengono poi ignorati. Tuttavia ci sono altre situazioni in cui gli spazi sono ugualmente esclusi in modo predefinito, come nell'esempio già visto, dove la stringa di conversione è composta solo da specificatori di conversione. Nell'esempio seguente, invece, si dimostra l'uso di caratteri estranei agli specificatori di conversione:

...
printf ("Inserisci la data: ");
scanf ("%d/%d/%d", &giorno, &mese, &anno);
...

In questo caso la digitazione della data richiede anche l'inserzione delle barre oblique, senza le quali il riconoscimento fallisce.

Purtroppo, la sintassi per la scrittura delle stringhe di conversione non è molto soddisfacente ed è difficile avere un'idea chiara del loro utilizzo. Pertanto, è consigliabile di utilizzare sempre solo modelli molto semplici.

585.6   Rappresentazione degli specificatori di conversione

Di norma, la scelta dello specificatore di conversione determina il tipo di dati dell'argomento (ovvero il tipo di variabile a cui l'argomento punta) e il modo in cui deve essere interpretato. La tabella successiva elenca alcuni degli specificatori di conversione utilizzabili, nella loro forma più semplice. È bene ricordare che anche in questo caso si può usare uno specificatore costituito dall'unione di due caratteri percentuali (%%), il quale identifica semplicemente un carattere di percentuale singolo proveniente dai dati in ingresso, ma da ignorare.

Tabella 585.15. Tipi di conversione principali.

Simbolo Corrispondenza
%...c
Corrisponde a un carattere, oppure, se viene specificata una lunghezza, a una sequenza di caratteri; pertanto, in condizioni normali il puntatore deve essere di tipo char * e, a partire dalla posizione indicata dallo stesso, ci deve essere abbastanza spazio per contenere tutti i caratteri previsti per l'immissione.
%...s
Corrisponde a una sequenza di caratteri diversi da quelli che producono uno spazio e a questa sequenza viene aggiunto automaticamente il carattere <NUL>, ovvero \0; pertanto, in condizioni normali il puntatore deve essere di tipo char * e, a partire dalla posizione indicata dallo stesso, ci deve essere abbastanza spazio per contenere la stringa da immettere, incluso il carattere nullo conclusivo.
%...d
Corrisponde a un numero intero (con o senza segno), espresso in base dieci; pertanto, in condizioni normali il puntatore deve essere di tipo int *, con o senza segno.
%...i
Corrisponde a un numero intero (con o senza segno), espresso in base otto, in base dieci o in base sedici (purché la base di numerazione sia riconoscibile dal prefisso usato); pertanto, in condizioni normali il puntatore deve essere di tipo int *, con o senza segno.
%...u
Corrisponde a un numero intero senza segno, espresso in base dieci; pertanto, in condizioni normali il puntatore deve essere di tipo unsigned int *.
%...x
Corrisponde a un numero intero senza segno, espresso in base sedici; pertanto, in condizioni normali il puntatore deve essere di tipo unsigned int *.
%...o
Corrisponde a un numero intero senza segno, espresso in base otto; pertanto, in condizioni normali il puntatore deve essere di tipo unsigned int *.
%...e
%...f
%...g
Corrisponde a un numero a virgola mobile rappresentato in notazione decimale fissa o in notazione esponenziale; pertanto, in condizioni normali il puntatore deve essere di tipo double *.

Tabella 585.16. Alcuni modificatori dell'ampiezza del valore da immettere.

Simbolo Corrispondenza
%...hd
%...hi
%...hu
%...ho
%...hx
La lettera h, prima di quella che specifica il tipo di conversione, quando si tratta dell'inserimento di un numero intero, specifica che il dato va trattato in qualità di short int *, con o senza segno, in base al contesto.
%...ld
%...lu
%...lo
%...lx
La lettera l, prima di quella che specifica il tipo di conversione, quando si tratta dell'inserimento di un numero intero, specifica che il dato va trattato in qualità di long int *, con o senza segno, in base al contesto.
%...lc
%...ls
La lettera l, prima di quella che specifica il tipo di conversione, quando si tratta dell'inserimento di un carattere o di una stringa, specifica che il dato va trattato in qualità di carattere esteso (wide char) o di stringa estesa (wide string). Di conseguenza, si intende che l'argomento sia di tipo wchar_t *.
%...Le
%...Lf
%...Lg
La lettera L, prima di quella che specifica il tipo di conversione, quando si tratta dell'inserimento di un valore in virgola mobile, specifica che il dato va trattato in qualità di long double *.

585.7   Funzioni per l'interpretazione dell'input

Tutte le funzioni standard il cui nome finisce per scanf interpretano dei dati in ingresso attraverso una stringa di conversione, secondo le modalità descritte nel capitolo, ovvero in modo analogo a scanf() che, in particolare, legge i dati da interpretare dallo standard input. In particolare, la funzione fscanf() legge l'input attraverso il flusso di file che costituisce il parametro stream (il primo argomento) e la funzione sscanf() legge l'input da una stringa (che costituisce sempre il primo argomento).

int fscanf (FILE *restrict stream, const char *restrict conversione, ...);
int sscanf (const char *restrict s, const char *restrict conversione, ...);
int scanf  (const char *restrict conversione, ...);

Le funzioni di cui è appena stato mostrato il modello sintattico, utilizzano gli argomenti successivi alla stringa di conversione in base a quanto indicato con gli specificatori di conversione. Altre funzioni equivalenti, con il nome che inizia con la lettera «v», hanno bisogno di un puntatore di tipo va_list:

int vfscanf (FILE *restrict stream, const char *restrict conversione, va_list arg);
int vsscanf (const char *restrict s, const char *restrict conversione, va_list arg);
int vscanf  (const char *restrict conversione, va_list arg);

1) Questa è una semplificazione, perché ci sono altre funzioni dello stesso gruppo, che iniziano con la lettera v, le quali alla fine hanno un puntatore di tipo va_list.

2) Questa è una semplificazione, perché ci sono altre funzioni dello stesso gruppo, che iniziano con la lettera v, le quali alla fine hanno un puntatore di tipo va_list.


Appunti di informatica libera 2008 --- Copyright © 2000-2008 Daniele Giacomini -- <appunti2 (ad) gmail·com>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome c_conversione_di_input_e_output.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home]

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory