简体   繁体   中英

scanf and strtok not working properly for me

I'm trying to write a small menu based program to maintain records. The user inputs a number to be used for the total amount of people to be stored (first name, last name, score). User enters info all on one line, separated by spaces, and I split them up into 3 columns (first name, last name, score), then presses enter and continues to enter more info until the max people is hit.

My problem being, when I run it, it doesn't work properly; it runs and accepts user input, but only for two students (even though I've been using numbers greater than 5 for test cases), then the program immediately ends (no error codes, just ends...) and it doesn't even make it to the menu. Can someone tell me what is wrong with my code?

int i, j, count, numberPeople, temp, choice;
char people[15][3], tempArr[20];
char *token;

printf("Please indicate number of records you want to enter (min 5, max 15): ");
scanf("%d", &temp);

while ((temp > 15) || (temp < 5)) {
    printf("\nNumber not in specified range, try again.\n");
    printf("Please indicate number of records you want to enter (min 5, max 15): ");
    scanf("%d", &temp);
}


numberPeople = temp;


printf("\nEnter the first name, last name, and grade (put a space in between each): ");
    for (i = 0; i < numberPeople; i++) {
        fgets(tempArr, 20, stdin);

    token = strtok(tempArr, " ");

    for (j = 0; j < 3; j++) {
        while (token != NULL) {
            people[i][j] = *token;
            printf("%s\n", token); // this is here to as a test case to see if my data was being stored.
            token = strtok(NULL, " ");
        }
    }

}

EDITED: changed scanf to fgets

OUTPUT

Please indicate number of records you want to enter (min 5, max 15): 5

Enter the first name, last name, and grade (put a space in between each): firstname1 lastname1 85
firstname1
lastname1
85

firstname2 lastname2 84
firstname2
lastname2

Program ended with exit code: 0

One problem is you're using scanf() to read in the entire input line which has spaces between the first name, last name and grade:

for (i = 0; i < numberPeople; i++) {
    scanf("%s", tempArr);
}

scanf("%s", tempArr) quits reading as soon as it hits the first space. For this loop you want to use fgets() :

for (i = 0; i < numberPeople; i++) {
    fgets(tempArr, 20, stdin);
}

But as @Pooya notes, this string size is too small for what you're doing. Although you allocate the two dimensional array of students and information fields, you never allocate the string space to hold their names and grades:

char people[15][3]

If you're doing this on the stack, it becomes conceptually a third dimension:

char people[15][3][24]

After this scanf() , there's still a return character left in the buffer:

scanf("%d", &temp);

it probably should be cleared out. @KevinDTimm and @bruceg hint at a problem here:

for (j = 0; j < 3; j++) {
    while (token != NULL) {
        people[i][j] = *token;

But I don't think Kevin's suggestion accounts for the index j. @Weather_Vane suggests adding \\r\\n to the strtok() separator string:

token = strtok(NULL, " ")

Otherwise your grade string (last field) will have a dangling newline. Also, you need to make a copy of the token returned by strtok() , you shouldn't store it directly.

Putting all these suggestions together and cleaning/fixing anything else I ran into, I offer the following rework:

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

#define MINIMUM_STUDENTS 5
#define MAXIMUM_STUDENTS 15
#define DATA_FIELDS 3

#define MAXIMUM_LINE_LENGTH 100
#define MAXIMUM_DATA_LENGTH 50

int main(int argc, char **argv) {

    int numberPeople;
    char people[MAXIMUM_STUDENTS][DATA_FIELDS][MAXIMUM_DATA_LENGTH];

    printf("Please indicate number of records you want to enter (min %d, max %d): ", MINIMUM_STUDENTS, MAXIMUM_STUDENTS);

    scanf("%d", &numberPeople);

    while ((numberPeople < MINIMUM_STUDENTS) || (numberPeople > MAXIMUM_STUDENTS)) {
        printf("\nNumber not in specified range, try again.\n");
        printf("Please indicate number of records you want to enter (min %d, max %d): ", MINIMUM_STUDENTS, MAXIMUM_STUDENTS);
        scanf("%d", &numberPeople);
    }

    printf("\n");

    while ((getchar()) != '\n'); // flush the return (and anything else) after the number input above

    printf("Enter the first name, last name, and grade (put a space in between each): \n");

    for (int i = 0; i < numberPeople; i++) {
        char tempArr[MAXIMUM_LINE_LENGTH];

        fgets(tempArr, MAXIMUM_LINE_LENGTH, stdin);

        char *token = strtok(tempArr, " ");

        for (int j = 0; j < DATA_FIELDS && token != NULL; j++) {
            strncpy(people[i][j], token, MAXIMUM_DATA_LENGTH);

            // this is here to as a test case to see if my data was being stored.
            printf("%s\n", people[i][j]);

            token = strtok(NULL, " \r\n");
        }

    }

    // do what you need to do with the data here!

    return 0;
}

First off, use fgets() not scanf()

Next, you don't need the for (j=0; j<3; j++) part, that's handled by strtok . Instead use the following:

token = strtok(tempArr, " ");
while (token != NULL) {
    people[i][j] = *token;
    printf("%s\n", token); // this is here to as a test case to see if my data was being stored.
    token = strtok(NULL, " ");
}

According to your sample, your input takes about 24 character but you defined tempArr[20] . because you overwrite something else in memory if you scan something more than 20 characters, it is better to have tempArr[100] or any number that makes sense for your tests.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM