Sign in to follow this  
checchetto

Problemi con la portabilità di codice da winzoz

Recommended Posts

Ipotizzo che fpurge() non sia una buona soluzione perché se ci sono altri programmi collegati allo stesso flusso di input cancellando il contenuto del buffer corro il rischio di cancellare anche dei dati destinati a loro?

Come soluzione alternativa ci può essere questa:

#include <stdio.h>#include <stdlib.h>int main (int argc, char *argv[]) {        char *stringa;        int j, len, dim;        printf("Quanti caratteri si vuole inserire? ");        scanf("%d%*c", &dim);        stringa = (char *)calloc((dim+1), sizeof(char));        printf("Inserire i caratteri -->\n");        fflush(stdout);        /* ciclo for per permettere all'utente di inserire il numero di caratteri desiderati */        for(j=0; j<dim; j++) {                /* fpurge() viene utilizzato per pulire il buffer in entrata */                scanf("%c%*c", &stringa[j]);        }        /* viene inserito \0 alla fine della stringa che equivale a NULL */        stringa[j] = '\0';        printf("La stringa costruita e' \"%s\".\n", stringa);        return 0;}

Inserendo %*c nel formato di conversione della scanf() si dice di effettuare la conversione (e quindi leggere l'a capo dal flusso di input) ma di non assegnarlo a niente.

Share this post


Link to post
Share on other sites

No, lo stream è un'astrazione creata dalla libreria C a livello del programma; se anche hai più processi che leggono la stessa risorsa, i buffer di stdio sono distinti. Il vero problema è che, se sul file descriptor 0 non hai un terminale, stdin è block-buffered, non line-buffered. Quindi capita questo:

./test <<EOF> 3> a> b> c> EOFQuanti caratteri si vuole inserire ? Inserire i caratteri -->La stringa costruita e'
La tua soluzione comunque è giusta, leggere esplicitamente i newline. Per far le cose proprio benino l'ideale sarebbe scartare tutti i caratteri extra fino alla fine di una riga, così:

scanf("%*[^\n]");scanf("\n");

Share this post


Link to post
Share on other sites

No, lo stream è un'astrazione creata dalla libreria C a livello del programma; se anche hai più processi che leggono la stessa risorsa, i buffer di stdio sono distinti.

Scusi prof, ma proprio l'unica domanda che non ho studiato doveva farmi?!? :o

Il vero problema è che, se sul file descriptor 0 non hai un terminale, stdin è block-buffered, non line-buffered. Quindi capita questo:

./test <<EOF> 3> a> b> c> EOFQuanti caratteri si vuole inserire ? Inserire i caratteri -->La stringa costruita e'

Tra l'altro non conoscevo l'utilizzo dell'operatore << della shell… ora sì! :D

La tua soluzione comunque è giusta, leggere esplicitamente i newline. Per far le cose proprio benino l'ideale sarebbe scartare tutti i caratteri extra fino alla fine di una riga, così:

scanf("%*[^\n]");scanf("\n");

Già, per fare le cose per benino bisogna fare quello che hai detto, però la soluzione che hai fornito non funziona... o meglio, io non sono riuscito ad applicarla ed ottenere il comportamento previsto, ogni volta occorre dare un input di troppo per terminare il programma. :eek:

Credo sia perché, tranne in alcuni casi, prima di operare una conversione scanf() elimina tutti gli spazi, le tabulazioni e i newline già presenti e quindi la chiamata "scanf("\n");" non riesce mai a leggere il newline quando dovrebbe. Ma in realtà non ho ben capito ancora come agisce la scanf()... sto ancora facendo delle prove, fra un po' posto un test case e spero che me lo saprai spiegare.

Nel frattempo ho trovato queste soluzioni alternative funzionanti e che scartano tutti i caratteri extra:

Soluzione 1

#include <stdio.h>#include <stdlib.h>int main (int argc, char *argv[]) {        char *stringa;        int j, dim;        printf("Quanti caratteri si vuole inserire? ");        scanf("%d%*[^\n]", &dim);        scanf("%*c");        stringa = (char *)calloc((dim+1), sizeof(char));        printf("Inserire i caratteri -->\n");        /* ciclo for per permettere all'utente di inserire il numero di caratteri desiderati */        for(j=0; j<dim; j++) {                scanf("%c%*[^\n]", &stringa[j]);                scanf("%*c");        }        /* viene inserito \0 alla fine della stringa che equivale a NULL */        stringa[j] = '\0';        printf("La stringa costruita e' \"%s\".\n", stringa);        return 0;}

Soluzione 2

#include <stdio.h>#include <stdlib.h>int main (int argc, char *argv[]) {        char *stringa;        int j, dim;        printf("Quanti caratteri si vuole inserire? ");        scanf("%d%*[^\n]", &dim);        stringa = (char *)calloc((dim+1), sizeof(char));        printf("Inserire i caratteri -->\n");        /* ciclo for per permettere all'utente di inserire il numero di caratteri desiderati */        for(j=0; j<dim; j++) {                scanf(" %c%*[^\n]", &stringa[j]);        }        /* viene inserito \0 alla fine della stringa che equivale a NULL */        stringa[j] = '\0';        printf("La stringa costruita e' \"%s\".\n", stringa);        return 0;}

Share this post


Link to post
Share on other sites

Già, per fare le cose per benino bisogna fare quello che hai detto, però la soluzione che hai fornito non funziona... o meglio, io non sono riuscito ad applicarla ed ottenere il comportamento previsto, ogni volta occorre dare un input di troppo per terminare il programma. :eek:

Ooops, è vero. Non avevo fatto caso al bug perché avevo testato il programma in maniera non interattiva, buttando dentro un input già scritto: in questo modo la scanf("\n") termina al primo carattere non whitespace. In effetti funziona anche dando l'input da tastiera, se uno ignora il prompt e scrive l'input alla cieca.

Credo sia perché, tranne in alcuni casi, prima di operare una conversione scanf() elimina tutti gli spazi, le tabulazioni e i newline già presenti e quindi la chiamata "scanf("\n");" non riesce mai a leggere il newline quando dovrebbe.

L'eliminazione del whitespace viene fatta prima di una conversione (%...), non prima di una stringa letterale. Il problema è che newline è un carattere whitespace, e viene interpretato come "mangia tutti i blank da qui in poi", quindi scanf("\n") non termina finché non vede sul buffer qualcosa che non è whitespace.

Per leggere esattamente un carattere newline la soluzione è questa:

scanf("%*1[\n]");

Ovvero usare un set [] con solo il newline, limitare a 1 la lunghezza, e ignorare il risultato della conversione.

Share this post


Link to post
Share on other sites

L'eliminazione del whitespace viene fatta prima di una conversione (%...), non prima di una stringa letterale. Il problema è che newline è un carattere whitespace, e viene interpretato come "mangia tutti i blank da qui in poi", quindi scanf("\n") non termina finché non vede sul buffer qualcosa che non è whitespace.

Già, ieri mi ero perso questo dettaglio fra le pieghe del man, ma stanotte giuro che ho ownato scanf() come si deve! :D

Per leggere esattamente un carattere newline la soluzione è questa:

scanf("%*1[\n]");

Ovvero usare un set [] con solo il newline, limitare a 1 la lunghezza, e ignorare il risultato della conversione.

Se voglio eliminare tutti i caratteri fino alla fine della riga e il carattere di newline, c'è qualche differenza fra usare scanf("%*[^\n]%*1[\n]"); o scanf("%*[^\n]%*c");, a parte le preferenze personali di forma?

Io non ne ho trovate…

Ah, e la memoria puntata da stringa in teoria andrebbe deallocata con free(stringa); prima di terminare il programma. :)

Share this post


Link to post
Share on other sites

Se il problema è la lettura di un carattere sporco, cioè inutile, ad esempio un new line questo deve essere scaricato...per esempio, al'interno di un ciclo while leggi n volte un carattere inserito volta per volta da tastiera.

Quindi ad ogni passo ci sarà una scanf tipo scanf("%c",&carat);

Se tu lasciassi il ciclo così com'è il carattere verrebbe letto ogni due passi perchè rimarrebbe in memoria il new line che premi ogni volta e lo caricherebbe alla successiva scanf...ora questo non è vero in tutti i casi, ma tralasciamo le finezze e concentriamo su come si potrebbe forse(se ho capito il problema)risolvere il problema...al fondo di ogni ciclo while dovresti inserire un getchar() per poter scaricare il new line...

Esempio di una funzione che ho fatto tempo fa...

void carica_valore(tipo_nodi *punt_elem)

{

char valore;

scanf("%c",&valore);

if(valore=='T')

{

(*punt_elem).valore=TRUE;

}

else

{

(*punt_elem).valore=FALSE;

}

(*punt_elem).calcolata=TRUE;

getchar(); <--SCARICO IL NEW LINE...

return;

}

Con la gets al di là di non risolvere il problema se ne creerebbe un alto cioè se ricordo bene la stringa memorizzata dal gets comprende il new line al suo interno quindi per scaricarlo si deve inserire un \0(tappo) nel penultimo carattere della stringa...un casino...

Comunque non sono sicuro di aver capito il tuo problema...

Share this post


Link to post
Share on other sites

Se il problema è la lettura di un carattere sporco, cioè inutile, ad esempio un new line questo deve essere scaricato...per esempio, al'interno di un ciclo while leggi n volte un carattere inserito volta per volta da tastiera.

Quindi ad ogni passo ci sarà una scanf tipo scanf("%c",&carat);

Si ma utilizzare scanf() come abbiamo indicato credo sia più efficiente ed è sicuramente più pulito.

Comunque non sono sicuro di aver capito il tuo problema...

Il problema è leggere il primo carattere e scartare tutti gli (eventuali) successivi caratteri fino alla fine della riga e il carattere di newline.

La soluzione (o meglio, le soluzioni) le abbiamo già fornite io e Ottimio.

Share this post


Link to post
Share on other sites

Se voglio eliminare tutti i caratteri fino alla fine della riga e il carattere di newline, c'è qualche differenza fra usare scanf("%*[^\n]%*1[\n]"); o scanf("%*[^\n]%*c");, a parte le preferenze personali di forma?

Sono entrambe sbagliate. Se la riga è vuota (= comincia con un newline), la conversione %*[^\n] fallisce (deve coprire almeno un carattere) e scanf termina prima di aver letto il newline. Perciò devi usare due chiamate separate:

scanf("%*[^\n]");scanf("%*1[\n]");
L'ultima riga la puoi sostituire con scanf("%*c"), visto che il primo carattere sullo stream a quel punto sarà sempre un newline, però scrivendo come sopra forse è più chiaro l'intento.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this