Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

scanf domanda

12 views
Skip to first unread message

mirko ballarini

unread,
Nov 13, 2023, 4:14:56 PM11/13/23
to
Ciao,
per errore ho scritto una scanf come sotto, ottenendo risultati bizzarri:

while(fscanf(Fin, "%s, %d, %d", stringa, &matricola, &voto)!= EOF) {
printf("Nome = %s\nMatricola = %d\nVoto = %d\n", stringa, matricola, voto);
}

Si notino le "," fra i %s e %d, che ovviamente non ci vanno.
Volevo chiedere, che effetto ha l'inserimento delle virgole nello scanf?
Sembrano ritornare degli indirizzi di memoria.
Senza le virgole il programma funziona come dovrebbe.

grazie



file da cui legge:
Ballarini 1234 30
Creolo 4354 12
Ficarra 6544 23
Picone 7756 15

Output con le virgole:
Nome = Ballarini
Matricola = 1
Voto = 1058674984
Nome = 1234
Matricola = 1
Voto = 1058674984
Nome = 30
Matricola = 1
Voto = 1058674984
Nome = Creolo
Matricola = 1
Voto = 1058674984
Nome = 4354
Matricola = 1
Voto = 1058674984
Nome = 12
Matricola = 1
Voto = 1058674984
Nome = Ficarra
Matricola = 1
Voto = 1058674984
Nome = 6544
Matricola = 1
Voto = 1058674984
Nome = 23
Matricola = 1
Voto = 1058674984
Nome = Picone
Matricola = 1
Voto = 1058674984
Nome = 7756
Matricola = 1
Voto = 1058674984
Nome = 15
Matricola = 1
Voto = 1058674984

Process finished with exit code 0

enoquick

unread,
Nov 13, 2023, 10:09:37 PM11/13/23
to
Il 13/11/23 15:14, mirko ballarini ha scritto:
> Ciao,
> per errore ho scritto una scanf come sotto, ottenendo risultati bizzarri:
>
> while(fscanf(Fin, "%s, %d, %d", stringa, &matricola, &voto)!= EOF) {
> printf("Nome = %s\nMatricola = %d\nVoto = %d\n", stringa, matricola, voto);
> }
>
> Si notino le "," fra i %s e %d, che ovviamente non ci vanno.
> Volevo chiedere, che effetto ha l'inserimento delle virgole nello scanf?
> Sembrano ritornare degli indirizzi di memoria.
> Senza le virgole il programma funziona come dovrebbe.
>
> grazie
>
>
[CUT]

Semplice, non essendo un % o spazio viene letto il carattere
specificato e scartato
Se il carattere letto non coincide lo scan termina


#include <stdio.h>

int main() {

const char p[]="56 678";
int x=99,y=98;
if (sscanf(p,"%d 6 %d",&x,&y) == EOF) {
printf("scanf error\n");
}
printf("%d %d\n",x,y);
return 0;
}

output:
56 78



if (sscanf(p,"%d 7 %d",&x,&y) == EOF) {

output: 56 98

come si puo vedere dall'output y non e' cambiato



|___|

jak

unread,
Nov 14, 2023, 7:50:31 AM11/14/23
to
mirko ballarini ha scritto:
Ciao,
la risposta alla tua domanda sarebbe: se metti una virgola nella stringa
di parsificazione, la fscanf si aspetta di trovarla durante lo scan e se
fossi stato meno 'lasco' col test di fine loop te ne saresti accorto da
te.
La fscanf ritorna EOF per fine file ma anche se non riesce a trovare dei
dati che soddisfino almeno la prima delle formattazioni. Nel tuo caso
legge il primo nome, non trova la virgola, fallisce il resto della
parsificazione ed, essendo in un loop, ricomincia a leggere dal punto in
cui era arrivata con la preceente chiamata, trova '1234' lo acquisisce
come primo parametro (Nome), poi si aspetta una virgola che non trova e
tutto si ripete acquisendo '30' come primo parametro e così via. Ciò che
stampi per 'Matricola' e 'Voto' non è significativo perché la fscanf,
fallendo per l'assenza della virgola, non vi ha scritto nulla.
Sarebbe, quindi, buona norma verificare che la fscanf abbia
effettivamente letto tutti i campi che ci si aspetta:

while((ret = fscanf(fp, "...", nome, &matr, &voto)) == 3)

Verrebbe da dire: beh, allora aggiungo le virgole nei file di dati e
tutto dovrebbe funzionare. Purtroppo non basta perché con la stringa di
parse che hai usato:

"%s, %d, %d"

la virgola verrebbe a far parte del Nome, quindi con:

Ballarini, 1234, 30

in Nome verrebbe caricato "Ballarini," (nota la virgola in fondo),
quindi la fscanf cercherebbe una virgola che ormai è stata letta, non la
trova più e fallisce. Per ovviare a questo problema si può sostituire %s
con qualcosa di meno generico che escluda la virgola:

"%[a-zA-Z], %d, %d"

in questo modo però bisogna tener conto del fatto che la virgola deve
seguire la parola ecquisita con %[a-zA-Z], cioè, ad esempio,
funzionerebbe con:

Ballarini, 1234, 30

ma non con:

Ballarini , 1234 , 30

siccome lo spazio in una riga di parse significa "0 o piò separatori
(spazi/tab), aggiungendo uno spazio prima della virgola il parse
funzionerebbe con la virgola in qualsiasi sistemazione:

Ballarini, 1234, 30
Ballarini , 1234 , 30
Ballarini,1234,30

a questo punto mettendo al posto della virgola qualcosa che ignori ciò
che non è un numero, come ad esempio:

"%[a-zA-Z]%*[^0-9]%d%*[^0-9]%d"

rende possibile separare i campi anche con altri separatori:

Ballarini, 1234;:30
Ballarini - 1234 / 30
Ballarini:1234;30

a questo punto perché non mettere la cigliegina sulla torta e fare in
modo che la fscanf non vada a prendere dati dalle linee successive? Ci
appendiamo anche il riconoscimento di fine linea e lo skip di quel che è
di troppo dopo i 3 valori che interessano:

" %[A-Za-z]%*[^0-9\n]%d%*[^0-9\n]%d%*[^\n] "

così dovrebbe funzionare correttamente anche con un file dati tipo:

Ballarini,1234:30
Creolo/4354/12
Ficarra-6544 23 tanto va la gatta...
Picone 7756 15

In ogni caso penso che non ti capiterà mai di vedere del codice
professionale dove venga usata una delle funzioni del gruppo fscanf.

mirko ballarini

unread,
Nov 14, 2023, 4:05:54 PM11/14/23
to

> così dovrebbe funzionare correttamente anche con un file dati tipo:
>
> Ballarini,1234:30
> Creolo/4354/12
> Ficarra-6544 23 tanto va la gatta...
> Picone 7756 15
>
> In ogni caso penso che non ti capiterà mai di vedere del codice
> professionale dove venga usata una delle funzioni del gruppo fscanf.


Grazie a tutti, stasera ho imparato cose nuove e ve ne sono grato


Giovanni

unread,
Nov 15, 2023, 11:45:55 AM11/15/23
to
On 11/14/23 13:50, jak wrote:
>
> In ogni caso penso che non ti capiterà mai di vedere del codice
> professionale dove venga usata una delle funzioni del gruppo fscanf.

Le funzioni della famiglia scanf() sono sicure solo se usate con
stringhe e/o file generati da programma che hanno un formato ben
definito ed immutabile. In questi casi se viene controllato che tutti
gli argomenti sian stati riconosciuti si ha una discreta affidabilità
nel loro uso.

Ciao
Giovanni
--
A computer is like an air conditioner,
it stops working when you open Windows.
< https://giovanni.homelinux.net/ >

jak

unread,
Nov 16, 2023, 8:19:46 AM11/16/23
to
Giovanni ha scritto:
> On 11/14/23 13:50, jak wrote:
>>
>> In ogni caso penso che non ti capiterà mai di vedere del codice
>> professionale dove venga usata una delle funzioni del gruppo fscanf.
>
> Le funzioni della famiglia scanf() sono sicure solo se usate con
> stringhe e/o file generati da programma che hanno un formato ben
> definito ed immutabile.

Anche perché se uno non riesce a leggere ciò che scrive, mi vien da
dire: chi ha colpa del suo mal pianga se stesso.

> In questi casi se viene controllato che tutti
> gli argomenti sian stati riconosciuti si ha una discreta affidabilità
> nel loro uso.
>

Ma devono essere proprio dati creati da un programma fatto apposta
complementarmente, perché già solo leggere un .csv prodotto da terzi
diventa un rischio. Basterebbe che chi lo produce, in un impeto di
bizzarrìa o perché sta catalogando poesie, decidesse di sostituire il
separatore standard ',' (utile nelle poesie) con un trattino '-' ed ecco
che, d'un tratto, alcune cifre facenti parte del record di ventano
negative :^D

Una "discreta affidabilità" è decisamente poco dal punto di vista
professionale. In effetti, più che un'opiniione, stavo esprimendo una
statistica: in olre 3 decaadi di codifica in C mi sarà capitato 2 o 3
volte di trovare funzioni del gruppo scanf in codice professionale. Ne
ho notato un largo uso, invece, nei programmini che io definisco 'di
servizio' che sempre più spesso, però, sono sostituiti da script shell,
perl, awk o python coi quali diventa semplice approfittare delle regex
con le quali il controllo può avvenire in modo più 'robusto'.

>
> Ciao
> Giovanni

Ciao.
0 new messages