第一个输入跳过,直接到下一个输入

时间:2016-12-15 07:36:58

标签: c

我遇到了一个简单登录程序代码的问题。我面临的问题是当我使用switch case或if语句作为Admin或User登录的选项时,会跳过username的输入并直接转到密码,无论我输入什么给我我的错误信息。相反,我希望它先接收我的用户名然后再输入密码。如果管理员或用户只有一个代码,只有一个代码,但是当有多个代码时,它可以自行运行。请帮忙。注意:我为管理员和用户使用相同的功能只是为了检查它是否有效。图为输出。我是C新手,也许是最小的行话?代码如下:

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
char username[18];
char pass[16];

void arequest()
{
    printf("\nPlease Enter username:");
    fflush(stdin);
    gets(username);

    printf("\nPlease Enter Password:");
    fflush(stdin);
    gets(pass);
}

void averify()
{
    if (strcmp(username, "admin") == 0)
{
    if (strcmp(pass, "apass") == 0)
        {
            printf("Successful Login");
            _getch();
        }
    else
        {
            printf("Invalid Password");
            _getch;
        }
    }
    else
    {
        printf("Invalid Username");
        _getch();
    }
}

int choice;
int main()
{
    printf("Welcome to Railway Reservation System");
    printf("\n1.Admin \n2.User");
    printf("\nPlease Enter your selection:");
    scanf_s("%d", &choice);
    if (choice == 1) 
    {
        arequest();
        averify();
    }
    else if (choice == 2) 
    {
        arequest();
        averify();
    }
    else 
    {
        printf("Invalid Choice");
        _getch();
        return main;
    }
    return 1;
} 

http://seek-for-android.github.io/repository/21/addon.xml

1 个答案:

答案 0 :(得分:0)

您正在使用fflush()刷新输入流。在大多数情况下,fflush(stdin)是未定义的行为,并且最多依赖于实现。要清除输入流中的额外字符,请考虑编写一个这样的小函数:

void clear_stream(void)
{
    int c;

    while ((c = _getch()) != '\n' && c != EOF)
        continue;
}

移除对fflush()的来电。由于gets(username)丢弃换行符,因此您无需在gets()之后清除该流。在clear_stream()中的这一行之后添加对main()的电话:

scanf_s("%d", &choice);

在调用scanf_s()之后,输入流中可能会有额外的字符,包括换行符,并且在尝试再次读取用户输入之前需要删除这些字符。在某些情况下,scanf()_s(和scanf())会跳过阅读输入中的初始空格,但_getch()getchar()则不会。这说明了使用scanf()的危险之一。

printf("\nPlease Enter your selection:");
scanf("%d", &choice);
clear_stream();

此外,gets()被认为是如此危险,以至于根本没有理由将其用于任何事情。请改用fgets()fgets()确实保留换行符,其中gets()丢弃了换行符,因此我经常使用gets()编写我自己的fgets()版本,这是安全的:

char * s_gets(char *st, int n)
{
    char *ret;
    int ch;

    ret = fgets(st, n, stdin);
    if (ret) {
        while (*st != '\n' && *st != '\0')
            ++st;
        if (*st)
            *st = '\0';
        else {
            while ((ch = getchar()) != '\n' && ch != EOF)
                continue;           // discard extra characters
        }
    }
    return ret;
}

conio.h是非标准的,函数_getch()scanf_s()也是如此。您应该使用stdio.h函数getchar()scanf()scanf()返回的值是成功分配的数量,您应该检查这一点以确保输入符合预期。在您的程序中,如果用户在选择提示符处输入了一个字母,则不会进行任何分配,并且choice的值仍未初始化。代码继续而不处理此问题。 choice可以初始化为某个合理的值,例如int choice = -1;。或者,您可以检查scanf()的返回值,看看是否已完成作业,然后继续进行。

我注意到你return来自main()。除非出现错误,否则您应该return 0。并且,如果选择无效,我会看到您return main。也许你的意思是return 1?您似乎忘记了#include <string.h>功能的strcmp()

最后,我不明白为什么usernamepasschoice是全局变量。这是一种不好的做法。这些应该在main()中声明,并根据需要传递给函数。对于#define全局常量MAXNAMEMAXPASS而言 是个好主意,而不是对数组维度进行硬编码。

当我开始时,我并不打算将其作为全面的代码审查,但这就是它的结果。以下是您的程序的修订版本,它实现了建议的更改:

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

#define MAXNAME  18
#define MAXPASS  16

void clear_stream(void)
{
    int c;

    while ((c = getchar()) != '\n' && c != EOF)
        continue;
}

char * s_gets(char *st, int n)
{
    char *ret;
    int ch;

    ret = fgets(st, n, stdin);
    if (ret) {
        while (*st != '\n' && *st != '\0')
            ++st;
        if (*st)
            *st = '\0';
        else {
            while ((ch = getchar()) != '\n' && ch != EOF)
                continue;           // discard extra characters
        }
    }
    return ret;
}

void arequest(char username[MAXNAME], char pass[MAXPASS])
{
    printf("\nPlease Enter username:");
    s_gets(username, MAXNAME);

    printf("\nPlease Enter Password:");
    s_gets(pass, MAXPASS);
}

void averify(char username[MAXNAME], char pass[MAXPASS])
{
    if (strcmp(username, "admin") == 0)
{
    if (strcmp(pass, "apass") == 0)
        {
            printf("Successful Login");
            getchar();
        }
    else
        {
            printf("Invalid Password");
            getchar();
        }
    }
    else
    {
        printf("Invalid Username");
        getchar();
    }
}


int main(void)
{
    char username[MAXNAME];
    char pass[MAXPASS];
    int choice;

    printf("Welcome to Railway Reservation System");
    printf("\n1.Admin \n2.User");
    printf("\nPlease Enter your selection: ");
    if (scanf("%d", &choice) == 1) {
        clear_stream();  
        if (choice == 1) 
        {
            arequest(username, pass);
            averify(username, pass);
        }
        else if (choice == 2) 
        {
            arequest(username, pass);
            averify(username, pass);
        }
        else 
        {
            printf("Invalid Choice: %d\n", choice);
            getchar();
            return 1;
        }
    } else {
        clear_stream();             // stream has not yet been cleared
        printf("Nonnumeric input");
        getchar();
    }

    return 0;
}

修改

评论中提到的OP scanf()导致Visual Studio出现问题。显然Visual Studio试图强制使用scanf_s()。这个函数的问题并不在于它本质上是坏的,只是它是非标准的。一种解决方案可能是使用已添加到代码中的s_gets()函数将用户选择读入字符缓冲区,然后使用sscanf()提取输入。这样做的好处在于,clear_stream()之后无需调用s_gets()函数,因为s_gets()会自行清除,因此clear_stream()函数现在可以完全删除来自该计划。这可以通过main()

中的少量更改来完成
char choice_buffer[10];
int choice;

...

if (s_gets(choice_buffer, sizeof(choice_buffer)) &&
    sscanf(choice_buffer, "%d", &choice) == 1) {
    if (choice == 1)

...

} else {
    printf("Nonnumeric input");
    getchar();
}

s_gets()将用户输入行的前9个字符(在本例中)读取到choice_buffer,这是一个将保存char的数组(还有更多) choice_buffer中的空格比保持单个数字选择和'\0'所需的空间大。如果出现错误,s_gets()将返回NULL指针,否则返回指向char的第一个choice_buffer的指针。如果s_gets()的返回值为非NULL,则sscanf()会将缓冲区中存储的第一个int分配给choice。如果在字符串中找不到int,则sscanf()返回值0,未通过条件测试。