[C] Conteggio delle parole di una stringa di caratteri

fabiett1
L'esercizio da svolgere è il seguente:

Esercizio 2. Si scriva un programma C che:
a. Acquisisca una stringa di massimo N caratteri (con N valore costante)
b. Ne manipoli il contenuto
i. Trasformando tutte le lettere minuscole in maiuscole
ii. Rimpiazzando tutti i caratteri non alfanumerici con il carattere ‘_’
iii. Sostituendo i caratteri numerici con il carattere ‘*’
c. Scandisca la stringa manipolata per contare quante parole sono
presenti al suo interno, considerando una o più occorrenze del
carattere ‘_’ come separatore tra parole.

I punti a. e b. sono riuscito a farli, ma il punto c. mi lascia un po' perplesso... il mio svolgimento dell'esercizio è il seguente:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 300
#include <string.h>
#include <ctype.h>

void leggiStringa(char* s1)
{
    printf("Inserire il testo da analizzare:\n");
    gets(s1);

    return;
}


void maiusc(char* s1)
{
    int i;

    for(i=0; s1[i]!='\0'; i++)
      {
          if(islower(s1[i]))
            s1[i]=toupper(s1[i]);
      }

      return;
}


void sost1(char* s1)
{
    int i=0;

    for(i=0; s1[i]!='\0'; i++)
    {
        if(isalnum(s1[i])==0)
            s1[i]='_';
    }

    return;
}


void sost2(char* s1)
{
    int i;

    for(i=0; s1[i]!='\0'; i++)
    {
        if(isdigit(s1[i])!=0)
            s1[i]='*';
    }

    return;
}


int numparole(char* s1)   \*CONTEGGIO DEL NUMERO DI PAROLE NELLA STRINGA*\
{
    int i, parole=0;

    for(i=0; s1[i]!='\0'; i++)
    {
        if((s1[i]=='_') && (s1[i-1]!='_'))
            parole++;
    }

    return parole;
}


int main()
{
    char s1[SIZE];

    leggiStringa(s1);

    printf("Codifica stringa:\n");
    maiusc(s1);
    sost1(s1);
    sost2(s1);
    printf("%s\n\n", s1);

    printf("Il numero di parole nella stringa e':%d", numparole(s1));

    return 0;
}


Il problema è che inserendo una frase mi conta una parola in meno di quelle effettivamente inserite nella stringa...

Risposte
Eduadie
Ed è giusto l'errore che hai.

Perchè per come hai impostato tu il conteggio delle parole, l'ultima non te la conterà mai poichè arrivato al carattere terminatore '\0" terminerà il ciclo e non si verificherà la condizione.

vict85
Ho identificato qualche bug e modificato alcune cose che potevano essere migliorate (secondo il mio parere ) . Oltre a questi miglioramenti avrei anche unificato tutte le modifiche in una sola funzione, ma ho preferito mantenere la struttura generale. Ho anche lanciato clang-format sul file, quindi la formattazione è un po' differente.

Nel codice ho dato per scontato che il codice potesse usare il C99 o il C90 con l'aggiunta delle estensioni più comuni (commenti come il C++ e possibilità di definire le variabili a piacimento). Se così non è, non è difficile eliminare i commenti e definire le variabili all'inizio del blocco.
#include <ctype.h>
#include <stdio.h>

// IMPROVEMENT
// Queste due non sono necessarie
// #include <stdlib.h>
// #include <string.h>


#define SIZE 300

// IMPROVEMENT
// conviene passare la dimensione del buffer
// void leggiStringa(char* s1)
void
leggiStringa( char *restrict s1, size_t const dim )
{
    // IMPROVEMENT
    // E' inutile chiamare printf se devi solo stampare una stringa
    // printf("Inserire il testo da analizzare:\n");
    puts( "Inserire il testo da analizzare:" );

    // BUG
    // ERRORE molto grave!!! Questa funzione e' stata eliminata dall'ultimo
    // standard.
    // Questa funzione può produrre problemi di sicurezza MOLTO seri!
    // gets(s1);
    fgets( s1, dim, stdin );

    // IMPROVEMENT
    // il return e' inutile
    // return;
}

// IMPROVEMENT
// conviene passare la dimensione del buffer
void
maiusc( char *restrict s1, size_t const dim )
{
    // IMPROVEMENT
    // dal C99 in poi il C permette di definire le variabili dove si preferisce
    // int i;

    // IMPROVEMENT
    // in teoria non è necessario ma e' meglio controllare di non uscire
    // dall'array
    for ( int i = 0; i != dim && s1[i] != '\0'; i++ ) {
        // IMPROVEMENT
        // non e' necessario testare prima che sia minuscola
        //   if(islower(s1[i]))
        //     s1[i]=toupper(s1[i]);
        s1[i] = toupper( s1[i] );
    }

    // IMPROVEMENT
    // il return e' inutile
    // return;
}

// IMPROVEMENT
// conviene passare la dimensione del buffer
void
sost1( char *restrict s1, size_t const dim )
{
    // IMPROVEMENT
    // dal C99 in poi il C permette di definire le variabili dove si preferisce
    // int i;

    // IMPROVEMENT
    // in teoria non è necessario ma e' meglio controllare di non uscire
    // dall'array
    for ( int i = 0; i != dim && s1[i] != '\0'; i++ ) {
        // IMPROVEMENT
        // Essere uguale a zero vuol dire falso nel C
        if ( !isalnum( s1[i] ) ) s1[i] = '_';
    }

    // IMPROVEMENT
    // il return e' inutile
    // return;
}

// IMPROVEMENT
// conviene passare la dimensione del buffer
void
sost2( char *restrict s1, size_t const dim )
{
    // IMPROVEMENT
    // dal C99 in poi il C permette di definire le variabili dove si preferisce
    // int i;

    // IMPROVEMENT
    // in teoria non è necessario ma e' meglio controllare di non uscire
    // dall'array
    for ( int i = 0; i != dim && s1[i] != '\0'; i++ ) {
        // IMPROVEMENT
        // Diverso da zero vuol dire vero nel C
        if ( isdigit( s1[i] ) ) s1[i] = '*';
    }

    // IMPROVEMENT
    // il return e' inutile
    // return;
}

// IMPROVEMENT
// conviene passare la dimensione del buffer
int numparole( char *restrict s1,
               size_t const dim ) /* BUG I commenti si scrivono con lo slash e non il backslash
                                     \* CONTEGGIO DEL NUMERO DI PAROLE NELLA STRINGA *\ */
{
    int i;
    int parole = 0;

    // BUG
    // Stai accedendo all'elemento -1 dell'array
    // Inoltre devi considerare il \0 come se fosse un _
    if ( dim == 0 || s1[0] == '\0' ) return 0;

    for ( i = 1; i != dim && s1[i] != '\0'; i++ ) {
        // if ( ( s1[i] == '_' ) && ( s1[i - 1] != '_' ) ) parole++;
        if ( s1[i] == '_' ) parole += ( s1[i - 1] != '_' );
    }
    if ( i == dim )
        return 0;
    else
        return parole + ( s1[i - 1] != '_' );
}

// IMPROVEMENT
// aggiunto il void per eliminare un warning di Pelles C
int
main( void )
{
    char s1[SIZE];

    leggiStringa( s1, SIZE );

    // IMPROVEMENT
    // E' inutile chiamare printf se devi solo stampare una stringa
    // printf("Codifica stringa:\n");
    puts( "Codifica stringa:" );

    maiusc( s1, SIZE );
    sost1( s1, SIZE );
    sost2( s1, SIZE );

    // IMPROVEMENT
    // E' inutile chiamare printf se devi solo stampare una stringa
    // printf("%s\n\n", s1);
    puts( s1 );
    puts( "" );  // questo serve per andare a capo una volta in più

    printf( "Il numero di parole nella stringa e':%d", numparole( s1, SIZE ) );

    // IMPROVEMENT
    // Lo standard C afferma che se raggiungi la fine del main senza
    // che si abbia alcun return allora il return 0 è implicito.
    // Quindi anche questo return 0 è inutile
    // return 0;
}


Qui trovi una versione senza commenti
#include <ctype.h>
#include <stdio.h>

#define SIZE 300

void
leggiStringa( char *restrict s1, size_t const dim )
{
    puts( "Inserire il testo da analizzare:" );
    fgets( s1, dim, stdin );
}

void
maiusc( char *restrict s1, size_t const dim )
{
    for ( int i = 0; i != dim && s1[i] != '\0'; i++ ) {
        s1[i] = toupper( s1[i] );
    }
}

void
sost1( char *restrict s1, size_t const dim )
{
    for ( int i = 0; i != dim && s1[i] != '\0'; i++ ) {
        if ( !isalnum( s1[i] ) ) s1[i] = '_';
    }
}

void
sost2( char *restrict s1, size_t const dim )
{
    for ( int i = 0; i != dim && s1[i] != '\0'; i++ ) {
        if ( isdigit( s1[i] ) ) s1[i] = '*';
    }
}

int
numparole( char *restrict s1, size_t const dim )
{
    if ( dim == 0 || s1[0] == '\0' ) return 0;

    int parole = 0;
    int i = 1;
    for ( ; i != dim && s1[i] != '\0'; i++ ) {
        if ( s1[i] == '_' ) parole += ( s1[i - 1] != '_' );
    }
    if ( i == dim )
        return 0;
    else
        return parole + ( s1[i - 1] != '_' );
}

int
main( void )
{
    char s1[SIZE];

    leggiStringa( s1, SIZE );

    puts( "Codifica stringa:" );

    maiusc( s1, SIZE );
    sost1( s1, SIZE );
    sost2( s1, SIZE );

    puts( s1 );
    puts( "" );

    printf( "Il numero di parole nella stringa e':%d\n", numparole( s1, SIZE ) );
}

fabiett1
Grazie mille, non avrei potuto ricevere una risposta più completa!

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.