:: Gruppi di Scansione ::
Possa fscanf con tutti i suoi derivati morire nella Geenna! Sciagura su di essi per 7 generazioni!
In questa pagina affronto lo spinoso argomento della fscanf. Perché spinoso? E che ne so, l'aggettivo suonava bene.
Tutto nacque da un problema di Lorenzo: (immaginate la sua voce)
"Yuck! Ho un file di log come il seguente, e devo scansionarlo:":
log.txt
www.donnebiotte.it 234
ftp.ilsitodeipornazzi.com 1982
www.alfredocimangia.com 9
www.cheballefscanf.com 9999
Voglio
- riconoscere il protocollo, cioè la parte prima del primo punto (www, ftp o quello che ci sarà)
- riconoscere il nome
- riconoscere l'estensione
- riconoscere il numero di visite
Ecco a voi la soluzione, con commenti finali
gruppo.c
#include <stdio.h>
int main() {
FILE * file;
char tipo[10];
char nome[20];
char estensione[20];
int valore;
file = fopen("log.txt", "r");
while (fscanf(file, "%[^.].%[^.].%[^ ] %d\n",tipo,nome,
estensione,&valore) !=EOF) {
printf("'s' 'd'\n", tipo,nome,estensione,valore);
/* etc. etc.*/
}
fclose(file);
return 0;
}
Il gruppo di scansione
La riga
fscanf(file, "%[^.].%[^.].%[^ ] %d\n",tipo,nome,estensione,&valore)
fa tutto il lavoro sporco.
Le parentesi quadre [ ]
circondano un cosiddetto gruppo di scansione. Come dice il professore, è un setaccio. Tutto ciò che viene messo tra le quadre, viene letto. Non appena si trova qualche cosa che NON appartiene a ciò che è contenuto tra le quadre, la scansione si ferma.
Quindi, una riga come
fscanf(file, "%[tetty]", stringa);
leggerà solo le stringhe che contengono una delle lettere contenute nella stringa tetty
, come per esempio:
tetty
ttteeettttyyttytytttteee
eeee
yyyyyy
ma non
tettamanzi
Non appena arriva alla prima a, la scansione si ferma.
Il gruppo di scansione invertito
Il gruppo di scansione funziona anche con l'accento circonflesso o quel diavolo che è, insomma, il ^
, e vuol dire: "tutto ciò che NON è contenuto nelle quadre, va bene. Non appena trovi qualche cosa contenuto nelle parentesi quadre, fermati".
Quindi, la riga
fscanf(file, "%[^abc]", stringa);
leggerà tutte le stringhe che NON contengono abc, cioè
defghi
lmnop
dddfdssdljfljwer
ma non
dario
Appena arriva alla a, si ferma, perché è contenuta nel gruppo di scansione.
Nel nostro caso, quindi, la parte
%[^.].%[^.].%[^ ] %d\n
vuol dire esattamente:
- Leggi tutto fino a che non trovi un punto, e mettilo nella prima variabile
- Leggi il punto
- Leggi quello che resta, fino al prossimo punto, e mettilo nella seconda variabile
- Leggi il secondo punto
- Leggi etc. terza variabile
- Spazio
- Leggi un intero e salvalo nella variabile
- Leggi un ritorno a capo (un finelinea)
Credo sia chiaro.
Complichiamoci un poco la vita
Se avessi usato una fscanf così:
fscanf(file, "%[^.]%*c%[^.]%*c%[^ ] %d\n", tipo,nome,estensione,&valore)
avrebbe funzionato ugualmente.
Il motivo è che ho introdotto una cosa diversa: il %*c
. Ciò vuol dire "leggi un carattere e fregatene, e passa al resto", e il resto lo conosciamo già.
Quindi, %*d
vuol dire "leggi un intero e fregatene", %*s
fregatene della stringa e così via.
I valori di ritorno di fscanf
Nel file ho scritto:
while(fscanf(...) != EOF) {
La fscanf
ritorna
- il numero di variabili correttamente lette
- oppure EOF se ha finito il file
Nel nostro caso, quindi, avrei anche potuto scrivere
while(fscanf(...) == 4) {
cioè, esegui il ciclo fintantoché fscanf legge correttamente 4 variabili per volta. E infatti avevo 4 variabili (andate sopra a vedere, se non ci credete).
Se avessimo trovato una riga, nel file log.txt, del tipo
...
www.negroni.it
...
senza un numero dopo, fscanf
avrebbe ritornato un 3, e il while avrebbe rilevato 3 != 4 e avrebbe terminato il ciclo.
Torna al Laboratorio di C | Programmazione