C - поддержка функции strtok()

Программное обеспечение разбивает файл на три файла: мужской, женский и ошибочный. Текстовый файл форматируется:

Имя, Фамилия, Возраст, Пол... Но разделены пробелом.

пример.txt выглядит так:

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

Я уже разбил его на мужчин и женщин, но я изо всех сил пытаюсь заставить его работать в зависимости от возраста... Вот код, любая помощь очень ценится.

/*
 * 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);
}

person Lewis Farnworth    schedule 18.07.2020    source источник
comment
Пожалуйста, отредактируйте вопрос и покажите первые 3-4 дословные строки файла example.txt.   -  person Jabberwocky    schedule 18.07.2020
comment
Спасибо за ваш ответ. Сбой fopen обрабатывается в строке 66, т.е. если какой-либо из указателей файлов возвращает NULL, выход с ошибкой... Кроме того, я отредактировал, чтобы показать первые строки файла   -  person Lewis Farnworth    schedule 18.07.2020
comment
@LewisFarnworth да, я видел это потом. Выполнять проверки после fopens странно, поэтому я пропустил это, но вы не предоставили первые 3-4 строки файла.   -  person Jabberwocky    schedule 18.07.2020
comment
@ user3121023 Отлично! Кажется, это решило проблему идеально, спасибо! Бармаглот, теперь, когда вы указали на это, это очень странно ... Реструктурировал его, чтобы он читался немного легче ... Спасибо за помощь, ребята: D   -  person Lewis Farnworth    schedule 18.07.2020
comment
Возможно, так и есть... Сейчас я посмотрю на это. Огромная помощь, ребята, ты!   -  person Lewis Farnworth    schedule 18.07.2020
comment
Функция: isFemale() не может проверить age человека. И гораздо лучшим названием было бы: `isFemaleOfSchoolAge()   -  person user3629249    schedule 19.07.2020
comment
относительно: if (gender == 'M' && age<=18 && age>=5) большинство детей идут в детский сад в возрасте 4, а не 5 лет   -  person user3629249    schedule 19.07.2020
comment
относительно: 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); } Гораздо лучше проверять возвращаемое значение при каждом вызове fopen() сразу после вызова fopen() Затем используйте такой код: if( ! fptrin ) { perror(fopen to read example.txt failed); выход(EXIT_FAILURE); } (продолжение)   -  person user3629249    schedule 19.07.2020
comment
(продолжение) в котором указывается, какой вызов fopen' failed AND outputs the text reason the system thinks the call failed, all to stderr. Remember, after one or more of the files are open and then a call to fopen()` завершается с ошибкой, необходимо закрыть открытые файлы перед выходом   -  person user3629249    schedule 19.07.2020
comment
относительно: // Read an integer and store read status in success Это не то, что делает вызов fgets(). Скорее он читает целую строку из входного файла   -  person user3629249    schedule 19.07.2020


Ответы (1)


следующий предлагаемый код:

  1. чисто компилирует
  2. выполняет желаемую функцию
  3. должным образом проверяет и обрабатывает ошибки от fopen()
  4. не включает заголовочные файлы, содержимое которых не используется.
  5. вызывает perror(), когда fopen() не выводит ни сообщение об ошибке, ни текст причины, по которой система считает, что ошибка произошла с stderr.

предостережение: OP должен добавить проверку, чтобы убедиться, что в каждой строке ввода достаточно полей. Предлагаемый код уже обрабатывает, когда в строке ввода найдено слишком много полей.

и теперь предлагаемый код:

#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);
}
person user3629249    schedule 19.07.2020
comment
Это обрабатывает плохие данные намного лучше, чем исходное решение, которое я написал... Спасибо за вклад! - person Lewis Farnworth; 22.07.2020