Utilizzo fgets, puts e fputs

di il
9 risposte

Utilizzo fgets, puts e fputs

Ciao, sotto bozza del libro ho provato a completare e rendere funzionale un programma che legga le stringhe da un file di input e, nel caso una stringa del file di input contenga al suo interno una stringa digitata nel programma, scrivi tutte quelle stringhe in un file di output. Tuttavia ho varie anomalie.
Prima di tutto usando nella funzione puts, gli output a video escono 2 volte.
Seconda cosa, il modo in cui la funzione agisce mi sembra un po' random..
A quanto ho letto, fgets legge finchè il file di testo non è finito (quindi è inutile specificare la condizione !feof(*filein)), o finchè nel file di testo in input o in stdin non si va a capo rigo (\n), e in questo caso memorizza la stringa in un array che viene reinizializzato dopo ogni ciclo.
Quindi anche se ci sono spazi o parole attaccate con in mezzo o ai margini la parola cercata dovrebbe funzionare allo stesso modo..

Il codice è:
#include <stdio.h>
#include <stddef.h>
#include <string.h>

#define OK 1
#define ERROR -1
#define MAXLINE 1000

int copiaselettiva (char refstr[]); //Prototipo

int main() {
    char check[MAXLINE];
    printf("Inserire stringa\n");
    fgets(check, MAXLINE, stdin);
    copiaselettiva(check);
    printf("Valore di ritorno: %d",copiaselettiva(check));
    return 0;
}

int copiaselettiva (char refstr[])
{
    char line[MAXLINE];
    FILE *fin, *fout;

    if((fin=fopen("D:\\File\\filein.txt", "r")) == NULL)
    {
        return ERROR;
    }


    if ((fout=fopen("D:\\File\\fileout.txt", "w"))== NULL)
    {
        fclose(fin);
        return ERROR;
    }


    /* fgets legge da filein al piu' MAXLINE-1 caratteri e assegna al vettore line i caratteri letti, incluso
     * l'eventuale carattere di newline, e termina la stringa con il carattere \0 */
    int n=1;
    /* //A ogni ciclo prendo una stringa dal file puntato da fin, a partire dalla prima avanzando finchè non ne sono
     * piu' rimaste, e le immagazzino in line */
    while (fgets(line, MAXLINE, fin)!= NULL && !ferror(fin))
        if (strstr(line,refstr)!= NULL) { //Se le stringhe tra quella cercata e quella nell'array non sono diverse
            printf("%d stringa\n",n);
            n++;
            puts(line); // Stampo la stringa a video
            fputs(line, fout); // Scrivo la stringa nel file di output
        }
    fclose(fin);
    fclose(fout);
    return OK;
}
Io come input nel programma do la stringa "ciao", senza virgolette.
Nel filein.txt ho
ciaomondo
Helloworld
ciao
mondociao
ciao ciao
ciao ciao Helloworld
ciaomondociao
mondociaomondo
ciao mondociao
mondociaomondo
A terminale viene stampato
Inserire stringa
ciao
1 stringa
ciao

2 stringa
mondociao

3 stringa
ciao ciao

4 stringa
ciaomondociao

5 stringa
ciao mondociao

1 stringa
ciao

2 stringa
mondociao

3 stringa
ciao ciao

4 stringa
ciaomondociao

5 stringa
ciao mondociao

Valore di ritorno: 1
Process finished with exit code 0
E ancora nel fileout.txt viene scritto:
ciao
mondociao
ciao ciao
ciaomondociao
ciao mondociao
Non riesco a trovare alcuna correlazione in tutto questo..
Grazie.

9 Risposte

  • Re: Utilizzo fgets, puts e fputs

    Mmm... Sostituendo la sottostringa "mondo" con la sottostringa "bella", ti sembrerà di essere al Salone del Libro di Torino. Uguale uguale.
  • Re: Utilizzo fgets, puts e fputs

    Perché chiami la funzione copiaselettiva due volte?
  • Re: Utilizzo fgets, puts e fputs

    A parte la battuta spiritosa della notte scorsa, eliminata la doppia chiamata occorre anche tenere conto che fgets() mantiene nella stringa letta il carattere di new line '\n', per cui non stai facendo la ricerca con "ciao", bensì con "ciao'\n'". Per aggirare l'ostacolo puoi usare una funzione tipo questa:
    int elimina_new_line_terminale( char *s )
    {
        int l = -1;
    
        if( NULL != s ) {
            l = strlen(s);
    
            if( 0!=l && '\n'==s[l-1] )
                s[--l] = '\0';
        }
    
        return l;
    }
    Anche main() va modificato:
    int main()
    {
        char check[MAXLINE];
        int esito;
    
        printf("Inserire stringa\n");
        fgets(check, MAXLINE, stdin);
    
        elimina_new_line_terminale(check);
        esito = copiaselettiva(check);
        printf("Valore di ritorno: %d",esito);
    
        return 0;
    }
  • Re: Utilizzo fgets, puts e fputs

    E' vero, non avevo fatto caso al fatto che printf effettuasse una nuova chiamata alla funzione.
    Riguardo alla funzione che mi hai passato, vorrei chiedere:
    Il fatto che vi sia scritto nel parametro formale (char *s) e non (char s[]) significa che stai usando un indirizzo al posto che copia-valore? Quindi operando su s[l] operi direttamente sulla stringa nel parametro attuale.
    In questo caso perchè assegni inizialmente a l il valore -1 e poi lo ritorni se nel main non vi è un assegnamento di quel valore? Non si potrebbe scrivere semplicemente
    void elimina_new_line_terminale( char *s )
    {
        int l;
    
        if( NULL != s ) {
            l = strlen(s);
    
            if( 0!=l && '\n'==s[l-1] )
                s[--l] = '\0';
        }
    }
    E per ultimo, come mai il primo rigo nel codice che avevo postato all'inizio veniva saltato? Dopotutto esso conteneva esattamente nella stringa "ciao\n", quindi dovrebbe comunque corrispondere alla richiesta.

    Grazie!
  • Re: Utilizzo fgets, puts e fputs

    XSlayer50 ha scritto:


    Il fatto che vi sia scritto nel parametro formale (char *s) e non (char s[]) significa che stai usando un indirizzo al posto che copia-valore?
    No ... sono due modi di scrivere per intendere la stessa cosa.
    veniva saltato
    A quale ti riferisci? Non si capisce
  • Re: Utilizzo fgets, puts e fputs

    Dato che nel main ha sol esplicitato la funzione e non la ha assegnata a nessuna variabile
    elimina_new_line_terminale(check);
    l'unica cosa che vedo è che ha eliminato il newline dell'array già esistente modificandolo per indirizzo, dato che il valore di ritorno della funzione non sembra venire usato. Che sono la stessa cosa buono a sapersi, vedo che in entrambi casi funziona per indirizzo e non copia valore.

    Riguardo a quello che veniva sempre saltato, mi riferisco alla prima riga del file in input. Veniva sempre saltata, anche se c'era solo la parola "ciao", con un'altra a capo rigo, che quindi era "ciao\n", e anche in quel caso non veniva copiata.
    Al contrario alcune parole contigue, senza \n, venivan copiate comunque..
  • Re: Utilizzo fgets, puts e fputs

    Il valore di ritorno della funzione che elimina '\n' in coda alla stringa vorrebbe avere due possibili significati:

    1) se maggiore o uguale a zero, indica la lunghezza della stringa dopo l'elaborazione
    2) se minore di zero indica che si è verificato un errore

    Il valore di ritorno di una funzione può anche essere ignorato, come in questo caso. Magari in altre circostanze quel dato può essere utile.

    Un'altra funzione, simile ma diversa, che uso di quando in quando è:
    /* =============================================================================
    int elimina_new_line_terminale( char *stringa );
    
    Parametri
    
        char *stringa   In ingresso, punta a uno spazio di memoria che contiene una
                        qualsiasi stringa C da analizzare e, eventualmente, da
                        modificare eliminando il carattere new line '\n' che si
                        dovesse trovare appena prima del terminatore '\0'.
    
    Valore di ritorno
    
        int             Un intero, da interpretare secondo questi significati...
    
                       -1: il parametro stringa e' NULL, l'operazione e' impossibile
                        0: il parametro stringa punta a una stringa valida che non
                           e' stato necessario modificare in quanto gia' in
                           partenza non presentava un carattere new line '\n' prima
                           del terminatore '\0'
                        1: il parametro stringa punta a una stringa valida, che e'
                           stata modificata eliminando il carattere new line '\n'
                           appena precedente il terminatore '\0'
    ==============================================================================*/
    
    int elimina_new_line_terminale( char *stringa ) {
        int esito = -1;
    
        if( stringa ) {
            int l = strlen(stringa);
            esito = l>0 && '\n'==stringa[l-1];
            if( esito ) stringa[--l] = '\0';
        }
    
        return esito;
    }
    In questo modo, se la stringa passata è stata ottenuta tramite fgets(), si può sapere se nello stream rimangono o non rimangono caratteri "pendenti" (quando le dimensioni del buffer usato in fgets() sono insufficienti, manca il new line in coda alla stringa, l'input è troncato e nello stream rimane una porzione dei dati immessi che potrebbe creare dei disguidi).

    Ad esempio, considera quest'altra funzione, se ti va:
    
    /*==============================================================================
    int chiedi_stringa_da_console( char *buff, size_t dim_buff, const char *msg );
    
    Parametri
    
        char *buff      In uscita, punta a uno spazio di memoria nel quale verra'
                        posta la stringa C immessa tramite console.
    
        size_t dim_buff In ingresso, specifica le dimensioni in byte dello spazio di
                        memoria puntato dal parametro buff.
    
        const char *msg In ingresso, punta a una stringa C che rappresenta un
                        messaggio da presentare in console al momento della
                        formulazione della richiesta. Se questo parametro e' NULL,
                        non viene mostrato alcun messaggio.
    
    Valore di ritorno
    
        int             Un intero. Va considerato come un valore booleano che
                        indica successo se vero e fallimento se falso.
    
    NOTA: la stringa collocata in buff non termina mai con un carattere new line
    '\n' appena precedente il carattere '\0'.
    ==============================================================================*/
    
    int chiedi_stringa_da_console( char *buff, size_t dim_buff, const char *msg ) {
        int esito = 0;
    
        if( NULL != buff ) {
            if( NULL == msg ) msg = "";
            printf( "%s", msg );
    
            if( fgets(buff,dim_buff,stdin) ) {
                esito = elimina_new_line_terminale( buff );
                if( 0 == esito ) while( '\n'!=getchar() ); // "svuota" lo stream di input
                esito = 1;
            }
        }
    
        return esito;
    }
    
    Nel contesto del tuo esercizio non te ne fai niente, ma a me è già capitato diverse volte di imbattermi in situazioni nelle quali ho trovato estremamente comodo "confinare" questo tipo di controlli in funzioni dedicate e poter fare affidamento su stringhe "pulite" (senza quel rompino del '\n' in coda).
  • Re: Utilizzo fgets, puts e fputs

    ciaomondo
    Helloworld
    ciao
    mondociao
    ciao ciao
    ciao ciao Helloworld
    ciaomondociao
    mondociaomondo
    ciao mondociao
    mondociaomondo
    Questo è il contenuto del file di input che ci hai segnalato. La stringa "ciao\n" compare alla fine della terza, della quarta, della quinta, della settima e della nona riga, il che porta all'output...
    ciao
    mondociao
    ciao ciao
    ciaomondociao
    ciao mondociao
  • Re: Utilizzo fgets, puts e fputs

    Hai ragione, svista mia. Grazie.
Devi accedere o registrarti per scrivere nel forum
9 risposte