[C] Leggere file di testo senza usare feof come condizione

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
Scrivi risposta
cnf

[C] Leggere file di testo senza usare feof come condizione

Messaggio da cnf »

Ciao a tutti. Sono un principiante del Linguaggio C e sto affrontando il seguente problema.

Supponiamo di avere il seguente file di testo fileditesto.txt

Codice: Seleziona tutto

$ A
$ B
$ C
111111
222222
333333
$ A
$ B
$ C
444444
555555
666666
e questo codice in C

Codice: Seleziona tutto

#include <stdio.h>

int main( void )
{
    FILE *read = fopen( "fileditesto.txt", "r" );

    /* controlla se il file è aperto correttamente */
    if ( read == NULL )
    {
        perror("Error in opening file");
        return(-1);
    }

    int num_dollar = 0;
    int num_lines = 0;
    int ch = 0;

    /* conta il numero di linee che contengono il carattere $ */
    while ( !feof(read) )       /* FIXME: svincolarsi dal feof */
    {
        ch = fgetc(read);
        if ( ch == '$' )
        {
            num_dollar++;
        }
        if ( ch == '\n' )
        {
            num_lines++;
        }
    }
    /* rewind( read ); */
    fclose(read);

    printf("Numero di dollari = %d\n",num_dollar);
    printf("Numero di linee   = %d\n",num_lines);

    return 0;
}
Come posso leggere il numero di linee e quelle che contengono il $ senza usare il controllo con !feof(...)? Ho letto che usare quest'ultimo può portare degli effetti collaterali e pertanto vorrei evitarlo, ma non so come.

Grazie per ogni risposta.
Avatar utente
SuperStep
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 2037
Iscrizione: lunedì 19 dicembre 2011, 16:26
Desktop: Unity
Distribuzione: Ubuntu 16.04 LTS x86_64
Sesso: Maschile
Località: Somma Vesuviana (NA)

Re: [C] Leggere file di testo senza usare feof come condizio

Messaggio da SuperStep »

esistono un'infinità di modi;

tanto per fare un esempio, supponiamo di leggere il file con la system call read finchè non è finito (quindi il numero di byte letti per ogni lettura è uguale a quelli passati come parametro). Per ogni lettura ci basta analizzare il buffer e cercare il carattere nuova linea "\n". Il numero di linee è la somma delle corrispondenze.

Tu che metodo cerchi?
ubuntu 16.04 LTS 64-bit - Memoria: 31,3 Gib - Processore: Intel Core i7-5960X CPU @ 3.00 GHz × 16 - Grafica: AMD Radeon HD 7800 Series - Disco: SSD 256 GB x 4 (RAID 01)
cnf

Re: [C] Leggere file di testo senza usare feof come condizio

Messaggio da cnf »

Per read intendi fread()?

Sul metodo al momento non ho preferenze. In realtà ho necessità di:
  • Contare il numero di linee che hanno un $ all'inizio
  • Contare il numero di linee non vuote
  • Ripuntare all'inizio del file per fare delle operazioni sulla base dei risultati ottenuti nei passi precedenti
Ultima modifica di cnf il mercoledì 17 giugno 2015, 12:19, modificato 1 volta in totale.
cnf

Re: [C] Leggere file di testo senza usare feof come condizio

Messaggio da cnf »

Ho trovato una possibile soluzione, ma vorrei una vostra opinione.

Supponiamo di avere questo file fileditesto.txt (notate le tre linee vuote alla fine)

Codice: Seleziona tutto

$ A
$ B
111111
222222
$ A
$ B
444444
555555



Ho provato un codice come questo

Codice: Seleziona tutto

#include <stdio.h>

int main(void)
{
    FILE *read = fopen( "fileditesto.txt", "r" );

    char line[100];
    int num_dollars = 0;
    int num_lines = 0;

    while( fgets(line, sizeof(line), read) != NULL )
    {
        num_lines++;
        if ( line[0] == '$' )
        {
            num_dollars++;
        }
        if ( line[0] == '\n' || line[0] == '\r' )
        {
            num_lines--;
        }
    }

    printf("D = %d\n",num_dollars);
    printf("L = %d\n",num_lines);

    fclose(read);

    return(0);
}
Sembra funzionare perché ottengo come output

Codice: Seleziona tutto

D = 4
L = 8
Due domande:
1. Può esserci qualche controindicazione in questo algoritmo?
2. Questo algoritmo può essere migliorato?
Avatar utente
M_A_W_ 1968
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 856
Iscrizione: venerdì 15 febbraio 2013, 3:57
Desktop: KDE
Distribuzione: SuSE
Sesso: Maschile
Località: Un luogo geometrico
Contatti:

Re: [C] Leggere file di testo senza usare feof come condizio

Messaggio da M_A_W_ 1968 »

cnf [url=http://forum.ubuntu-it.org/viewtopic.php?p=4769955#p4769955][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:Due domande:
1. Può esserci qualche controindicazione in questo algoritmo?
2. Questo algoritmo può essere migliorato?
La soluzione con la fgets() è la più diffusa, la più razionale e non ha controindicazioni di sorta, al contrario del ricorso alle system calls o ad altre soluzioni bislacche. Deve pertanto essere sempre adottata, a meno di esplicite esigenze particolari che costringano al ricorso ad altre soluzioni. Inoltre è corretto quanto hai letto, la feof() è di fatto una funzione deprecata e trova impiego solo rarissimamente.

Codesto "algoritmo" purtroppo non può essere migliorato in alcun modo: non esiste una scorciatoia efficace e universale alla lettura esaustiva del contenuto di un file, e trattasi di una delle operazioni inerentemente più lente tra quelle possibili su un calcolatore del mondo reale, come tutte quelle I/O-bound e in particolare quelle da e verso periferiche elettromeccaniche, le più lente in termini assoluti.

Ricordati comunque di controllare sempre che read (pessima scelta per un nome di variabile: meglio fpin o qualcosa di analogo) sia diverso da NULL dopo la fopen().
Sì, un blog ce l'ho perfino io: gli è che mi manca il tempo...

"...in una società che sembra sempre più spaventata dai problemi troppo articolati e che rigetta come un corpo estraneo ogni elemento di complessità, sapremo ancora come utilizzare il parere degli esperti?"
cnf

Re: [C] Leggere file di testo senza usare feof come condizio

Messaggio da cnf »

M_A_W_ 1968 [url=http://forum.ubuntu-it.org/viewtopic.php?p=4769972#p4769972][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto: Codesto "algoritmo" purtroppo non può essere migliorato in alcun modo: non esiste una scorciatoia efficace e universale alla lettura esaustiva del contenuto di un file, e trattasi di una delle operazioni inerentemente più lente tra quelle possibili su un calcolatore del mondo reale, come tutte quelle I/O-bound e in particolare quelle da e verso periferiche elettromeccaniche, le più lente in termini assoluti.
Ho notato però che la condizione

Codice: Seleziona tutto

if ( line[0] == '\n' || line[0] == '\r' ){...}
può essere sostituita anche con

Codice: Seleziona tutto

if ( line[0] == '\r' ){...}
Come mai?
M_A_W_ 1968 [url=http://forum.ubuntu-it.org/viewtopic.php?p=4769972#p4769972][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto: Ricordati comunque di controllare sempre che read (pessima scelta per un nome di variabile: meglio fpin o qualcosa di analogo) sia diverso da NULL dopo la fopen().
Grazie del suggerimento per quanto riguarda il nome della variabile, lo ignoravo totalmente. Per la fretta non ho inserito il controllo dopo fopen(...), ma se ho capito bene, è quello che ho scritto nel primo codice, giusto?
Avatar utente
M_A_W_ 1968
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 856
Iscrizione: venerdì 15 febbraio 2013, 3:57
Desktop: KDE
Distribuzione: SuSE
Sesso: Maschile
Località: Un luogo geometrico
Contatti:

Re: [C] Leggere file di testo senza usare feof come condizio

Messaggio da M_A_W_ 1968 »

cnf [url=http://forum.ubuntu-it.org/viewtopic.php?p=4769981#p4769981][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:
M_A_W_ 1968 [url=http://forum.ubuntu-it.org/viewtopic.php?p=4769972#p4769972][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto: Codesto "algoritmo" purtroppo non può essere migliorato in alcun modo: non esiste una scorciatoia efficace e universale alla lettura esaustiva del contenuto di un file, e trattasi di una delle operazioni inerentemente più lente tra quelle possibili su un calcolatore del mondo reale, come tutte quelle I/O-bound e in particolare quelle da e verso periferiche elettromeccaniche, le più lente in termini assoluti.
Ho notato però che la condizione

Codice: Seleziona tutto

if ( line[0] == '\n' || line[0] == '\r' ){...}
può essere sostituita anche con

Codice: Seleziona tutto

if ( line[0] == '\r' ){...}
Come mai?
Si tratta di una vera e propria FAQ. I vari SO mainstream usano convenzioni diverse per il delimitatore di linea, come ben spiegato ad esempio qui.

Il controllo di buon esito dell'operazione di apertura del file è sostanzialmente quello già presentato nel primo esempio di codice.

Codice: Seleziona tutto

    
    FILE *fpin;
...
    fpin = fopen(...);
    if (NULL == fpin)
    {
        perror("Error in opening file");
        return 1;
    }
Meglio evitare valori di ritorno negativi, anche se apparentenemente la sintassi lo consente. Si tratta di un vecchissimo problema di totale mancanza di interoperabilità e portabilità tra SO diversi, che ben tre standard del linguaggio (C'89, C'99, C'11) non sono in alcun modo riusciti ad appianare.

La sintassi pseudo-funzionale return(qualcosa), in tutte le sue varianti, non è in alcun modo errata ed ha anzi numerosi validi motivi stilistici a supporto del suo impiego (primo tra tutti l'omogeneità formale con altre funzioni di terminazione della libreria standard, e con altri linguaggi di programmazione di varie generazioni). Tuttavia è opportuno sapere che, trattandosi di una istruzione, a rigore molte norme di stile non incoraggiano tale utilizzo, pur non vietandolo tassativamente.

Anteporre la costante nel confronto entro la if() ha invece l'alto merito di scaricare sul compilatore il controllo di un potenziale errore semantico, nel caso fin troppo frequente in cui si dimentichi un segno '='. La costante non può essere un lvalue, e in caso di eventuali sviste sintattiche si avrà un errore in compilazione, non un semplice warning (spesso ignorato o disabilitato).
Ultima modifica di M_A_W_ 1968 il mercoledì 17 giugno 2015, 13:41, modificato 1 volta in totale.
Sì, un blog ce l'ho perfino io: gli è che mi manca il tempo...

"...in una società che sembra sempre più spaventata dai problemi troppo articolati e che rigetta come un corpo estraneo ogni elemento di complessità, sapremo ancora come utilizzare il parere degli esperti?"
cnf

Re: [C] Leggere file di testo senza usare feof come condizio

Messaggio da cnf »

Perfetto, grazie mille per tutte le spiegazioni e i suggerimenti.
Avatar utente
M_A_W_ 1968
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 856
Iscrizione: venerdì 15 febbraio 2013, 3:57
Desktop: KDE
Distribuzione: SuSE
Sesso: Maschile
Località: Un luogo geometrico
Contatti:

Re: [C] Leggere file di testo senza usare feof come condizio

Messaggio da M_A_W_ 1968 »

Aggiungo solo che, se sei seriamente interessato ad approfondire la questione della scelta di funzioni e idiomi in funzione della massima robustezza e razionalità stilistica (e sovente anche delle migliori prestazioni), esistono appositi e rispettati manuali ampiamente usati nella prassi e nella didattica. In particolare la seconda parte della bibliografia, dopo un paio di guide di stile generiche ed un testo sulla programmazione offuscata, si concentra sull'uso ottimizzato e razionale del linguaggio.

Inoltre potresti trovare utile la conoscenza delle regole MISRA/C (il primo passo per un uso razionale del linguaggio C in contesti critici) e l'uso di uno dei tanti parser gratuiti per tale set di regole.
Sì, un blog ce l'ho perfino io: gli è che mi manca il tempo...

"...in una società che sembra sempre più spaventata dai problemi troppo articolati e che rigetta come un corpo estraneo ogni elemento di complessità, sapremo ancora come utilizzare il parere degli esperti?"
cnf

Re: [C] Leggere file di testo senza usare feof come condizio

Messaggio da cnf »

Grazie mille per i riferimenti. Al momento sono (e credo che lo sarò per molto/sempre) un programmatore amatoriale; programmo per passione e per piccoli scopi di utilità. Ciononostante, darò uno sguardo a quanto mi hai segnalato.

Ancora grazie.
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: Bing [Bot] e 4 ospiti