【问题标题】:scanf and strtok not working properly for mescanf 和 strtok 不适合我
【发布时间】:2016-03-24 10:44:43
【问题描述】:

我正在尝试编写一个基于菜单的小程序来维护记录。 用户输入要用于存储的总人数(名字、姓氏、分数)的数字。用户在一行上输入所有信息,用空格分隔,我将它们分成 3 列(名字、姓氏、分数),然后按 Enter 并继续输入更多信息,直到达到最大人数。

我的问题是,当我运行它时,它不能正常工作;它运行并接受用户输入,但仅适用于两个学生(即使我一直在使用大于 5 的数字作为测试用例),然后程序立即结束(没有错误代码,只是结束......)并且它没有甚至进入菜单。谁能告诉我我的代码有什么问题?

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, " ");
        }
    }

}

已编辑:将 scanf 更改为 fgets

输出

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

【问题讨论】:

  • 为什么你的 for 循环中嵌套了一个 while 循环?
  • scanf("%s") 一次读取一个“单词”,以空格分隔。使用fgets() 读取整行,然后使用strtok() 将其拆分。您可以通过在阅读后立即打印出计算机读取的内容来帮助自己;这是最基本的调试形式,但仍然非常有价值;它会告诉您发生了什么(您为 2 个用户输入 6 个单词,因此在读取第二个数字之前满足循环到 5)。

标签: c


【解决方案1】:

一个问题是您使用scanf() 读取整个输入行,其中名字、姓氏和等级之间有空格:

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

scanf("%s", tempArr) 一碰到第一个空格就退出阅读。对于这个循环,你想使用fgets():

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

但正如@Pooya 所说,这个字符串大小对于你正在做的事情来说太小了。尽管您分配了学生和信息字段的二维数组,但您从未分配字符串空间来保存他们的姓名和成绩:

char people[15][3]

如果您在堆栈上执行此操作,它在概念上就变成了第三个维度:

char people[15][3][24]

在这个scanf()之后,缓冲区中还剩下一个返回字符:

scanf("%d", &temp);

它可能应该被清除。 @KevinDTimm 和 @bruceg 在这里暗示了一个问题:

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

但我认为 Kevin 的建议不能解释索引 j。 @Weather_Vane 建议将 \r\n 添加到 strtok() 分隔符字符串中:

token = strtok(NULL, " ")

否则,您的成绩字符串(最后一个字段)将有一个悬空的换行符。另外,您需要将strtok()返回的令牌复制一份,您不应该直接存储它。

将所有这些建议放在一起并清理/修复我遇到的任何其他问题,我提供以下返工:

#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;
}

【讨论】:

  • 请注意,您应该每次检查scanf()返回值(确保它是1),并且getchar()将在EOF上返回EOF并且这不是换行符,所以循环也可以运行很长时间。请注意,如果源字符串对于目标字符串来说太长,strncpy() 不保证空终止。您应该从fgets() 检测到EOF。您是否应该担心用户只在预期 Fred Bloggs 23 的地方输入 Fred?循环停止(这很好),但它不会报告数据丢失,这可能会更好。这要好得多,但仍有改进的余地。
  • @JonathanLeffler,我同意应该检查每个结果是否有错误——即使是这样的简单程序也需要对细节的不懈关注。我的目标是获得可以实际运行并分配所需内存的东西。 (令我惊讶的是,怎么可能在 C 中出现如此严重的内存分配错误,但仍然得到看起来接近工作的东西!叹息。)
【解决方案2】:

首先,使用fgets() 而不是scanf()

接下来,您不需要(j=0; j&lt;3; j++) 部分,它由strtok 处理。而是使用以下内容:

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, " ");
}

【讨论】:

  • 如果使用fgets,我会将\r\n 添加到分隔符字符串中。
【解决方案3】:

根据您的示例,您的输入大约需要 24 字符,但您定义了 tempArr[20]。因为如果您扫描的内容超过了20 个字符,则会覆盖内存中的其他内容,因此最好使用tempArr[100] 或任何对您的测试有意义的数字。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-01
    • 2014-04-20
    • 1970-01-01
    • 2017-08-09
    • 1970-01-01
    • 2011-08-11
    相关资源
    最近更新 更多