Lewis Farnworth
Lewis Farnworth

Reputation: 297

C - strtok() function assistance

Software splits a file into three files, male, female and erroneous. The text file is formatted:

First name, Last name, Age, Gender... But is delimited by space.

example.txt looks like this:

Tim Smith 18 M
Jonathon Jones 26 M
Kathy Black 13 F
Sarah Saxby 28 F

I've already gotten it to split based on Male or Female, but I'm struggling to get it to work based on age... Here's the code, any help is much appreciated.

/*
 * C program to split lines of text according to gender and age
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* Function declarations */
int isMale(char gender, int age);
int isFemale(char gender, int age);

/* Returns true (1) if the character in the last field is M and age is 4-18 */
int isMale(char gender, int age)
{
    if (gender == 'M' && age<=18 && age>=5)
    {
        // printf("Male %i \n", age);
        return 1;
    }
    return 0;
}

/* Returns true (1) if the character in the last field is F and age is 4-18 */
int isFemale(char gender, int age)
{
    if (gender == 'F') 
    {
        // printf("Female %i \n", age);
        return 1;
    }
    return 0;
}


int main()
{
    /* File pointer to hold reference to different files */
    FILE * fPtrIn,      // Input file
         * fPtrMale,    // Males of school age 
         * fPtrFemale,  // Females of school age
         * fPtrMisc;    // Data not within the given parameters

    // Open all files to perform read/write.
    fPtrIn       = fopen("data/example.txt", "r");
    fPtrMale     = fopen("data/males.txt" , "w");
    fPtrFemale   = fopen("data/females.txt"  , "w");
    fPtrMisc     = fopen("data/erroneus.txt", "w");
        
    // current_char is the current character being read
    char current_char;

    // hoping that too long lines won't come
    char line[300], line_parse[300];

    // Last field is where gender is stored, ret is the token used for strtok()
    char *last_field, *ret;

    // 0 or 1, if the age is outside or within the age limits
    int age;
    int field_count = 0;

    // fopen() return NULL if unable to open file in given mode
    if(fPtrIn == NULL || fPtrMale == NULL || fPtrFemale == NULL || fPtrMisc == NULL)
    {
        // Unable to open file, exit program print result
        printf("Unable to open file.\n");
        printf("Check file exists and permissions are correct.\n");
        exit(EXIT_FAILURE);
    }

    // File open success message
    printf("File opened successfully. \n\n");

    // Read an integer and store read status in success.
    while (fgets(line, sizeof(line), fPtrIn) != NULL)
    {
        // Copy the line for parsing
        strcpy(line_parse, line);

        // Separate the line into tokens
        last_field = ret = strtok(line_parse, " ");
        while (ret != NULL)
        {
            age = 0;
            last_field = ret;
            printf("%s \n", ret);

            if (field_count == 2)
            {
                age = atoi(ret);
            }

            field_count++;
            if (field_count == 4)
            {
                field_count = 0;
            }

            ret = strtok(NULL, " ");
        }

        // Get the first character of the last field
        if (last_field == NULL) current_char = '\0';
        else current_char = last_field[0];

        // Write each line to a separate file
        if (isMale(current_char, age))
            fputs(line, fPtrMale);
        else if (isFemale(current_char, age))
            fputs(line, fPtrFemale);
        else
            fputs(line, fPtrMisc);
    }

    // Close each file
    fclose(fPtrIn);
    fclose(fPtrMale);
    fclose(fPtrFemale);
    fclose(fPtrMisc);
    printf("Data written to files successfully. \n");

    return(0);
}

Upvotes: 1

Views: 73

Answers (1)

user3629249
user3629249

Reputation: 16540

the following proposed code:

  1. cleanly compiles
  2. performs the desired functionality
  3. properly checks for and handles errors from fopen()
  4. does not include header files those contents are not used.
  5. calls perror() when fopen() fails to output both the error message and the text reason the system thinks the error occurred to stderr.

caveat: OP should add check to assure enough fields found in each input line. The proposed code already handles when too many fields found in input line.

and now, the proposed code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <ctype.h>


#define MAX_LINE_LEN 300
#define MAX_FIELD_LEN 50

struct lineFields
{
    char firstName[ MAX_FIELD_LEN ];
    char lastName[ MAX_FIELD_LEN ];
    int  age;
    char gender;
};


/* Function declarations */
int isMale(char gender, int age);
int isFemale(char gender, int age);


/* Returns true (1) if the character in the last field is M and age is 4-18 */
int isMale(char gender, int age)
{
    if (gender == 'M' && age<=18 && age>=5)
    {
        // printf("Male %i \n", age);
        return 1;
    }
    return 0;
}


/* Returns true (1) if the character in the last field is F and age is 4-18 */
int isFemale(char gender, int age)
{
    if (gender == 'F' && age<=18 && age>=5) 
    {
        // printf("Female %i \n", age);
        return 1;
    }
    return 0;
}


int main( void )
{
    /* File pointer to hold reference to different files */
    FILE * fPtrIn,      // Input file
         * fPtrMale,    // Males of school age 
         * fPtrFemale,  // Females of school age
         * fPtrMisc;    // Data not within the given parameters

    // Open all files to perform read/write.
    fPtrIn       = fopen("data/example.txt", "r");
    if( !fPtrIn )
    {
        perror( "fopen to read input file failed" );
        exit( EXIT_FAILURE );
    }
    
    fPtrMale     = fopen("data/males.txt" , "w");
    if( !fPtrMale )
    {
        perror( "fopen to write male file failed" );
        exit( EXIT_FAILURE );
    }
    
    fPtrFemale   = fopen("data/females.txt"  , "w");
    if( !fPtrFemale )
    {
        perror( "fopen to write female file failed" );
        exit( EXIT_FAILURE );
    }
    
    fPtrMisc     = fopen("data/erroneus.txt", "w");
    if( !fPtrMisc )
    {
        perror( "fopen to write not of school age file failed" );
        exit( EXIT_FAILURE );
    }
    
    char line[ MAX_LINE_LEN ];
    char line_parse[ MAX_LINE_LEN ];    

    while( fgets( line, sizeof(line), fPtrIn ) )
    {
        // Copy the line for parsing
        strcpy(line_parse, line);

        struct lineFields fields;
        
        int fieldCount = 0;
        // Separate the line into tokens
        char * token = strtok(line_parse, " ");
        while ( token )
        {
            printf( "%s \n", token );

            switch( fieldCount )
            {
                case 0:
                    strcpy( fields.firstName, token );
                    break;
            
                case 1:
                    strcpy( fields.lastName, token );
                    break;
                    
                case 2:
                    fields.age = atoi( token );
                    break;

                case 3:
                    fields.gender = token[0];
                    break;

                default:
                    printf( "too many fields in input: %s\n", line );
                    break;
            }

            fieldCount++;

            token = strtok( NULL, " " );
        }

        // Write each line to a separate file
        if ( isMale( fields.gender, fields.age ) )
            fputs(line, fPtrMale);
        else if ( isFemale( fields.gender, fields.age ) )
            fputs(line, fPtrFemale);
        else
            fputs(line, fPtrMisc);
    }

    // Close each file
    fclose(fPtrIn);
    fclose(fPtrMale);
    fclose(fPtrFemale);
    fclose(fPtrMisc);
    printf("Data written to files successfully. \n");

    return(0);
}

Upvotes: 1

Related Questions