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

标签 c

我的简单登录程序代码遇到了问题。我面临的问题是,当我使用开关盒或if语句(以管理员或用户身份登录)时,用户名的输入将被跳过并直接输入密码,无论我输入什么,它都会给我我的错误讯息。相反,我希望它先接收我的用户名,然后再接收密码。如果只有“管理员”或“用户”的代码,而只有一个,而没有多个代码,则它不能单独工作。请帮忙。注意:我正在为管理员和用户使用相同的功能,只是为了检查它是否有效。图为输出,我是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;
} 


output

最佳答案

您正在使用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()的返回值以查看是否进行了赋值,然后进行相应的操作。

我注意到您从returnmain()正在1。除非出现错误,否则您应该return 0。而且,如果选择无效,我会看到您return main。也许您是要在这里return 1?看来您忘记了#include <string.h>功能。

最后,我不明白为什么strcmp()usernamepass是全局变量。这是一个坏习惯。这些应该在choice中声明,并根据需要传递给函数。 main()全局常量#defineMAXNAME而不是硬编码数组维是一个好主意。

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

#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 MAXPASS在Visual Studio中引起了问题。显然,Visual Studio尝试强制使用scanf()。此功能的问题不在于它本身是不好的,还在于它是非标准的。一种解决方案可能是使用已经添加到代码中的scanf_s()函数将用户选择读入字符缓冲区,然后使用s_gets()提取输入。这样做的优点是,无需在sscanf()之后调用clear_stream()函数,因为s_gets()会在其自身之后清除,因此现在可以将s_gets()函数从程序中完全删除。只需在clear_stream()中进行少量更改即可完成:

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();
}


main()最多读取用户输入到s_gets()的一行的前9个字符(在这种情况下),该数组将容纳choice_buffer s(char中的空间超出了所需的空间)按住一个数字和一个choice_buffer)。如果存在错误,则'\0'返回s_gets()指针,否则返回指向NULL的第一个char的指针。如果choice_buffer的返回值不是-s_gets(),则NULL将缓冲区中存储的第一个sscanf()分配给int。如果在字符串中未找到choice,则int返回值0,从而导致条件测试失败。

关于c - 跳过第一个输入,直接转到下一个输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41158761/

相关文章:

c - 使用数组指针对结构进行排序不起作用

c - 如何使用 sscanf 仅使用 stdio.h 确保输入的字符串长度为 1

c++ - new/malloc 或 delete/free 是否占用或使缓存行无效?

c - 如何分割和保存

c - 如何在函数内初始化和分配 char** 参数

c - 常量数组的全局变量的替代品?

c - Timespec 重新定义错误

c - ld : cannot find crt1. o:没有这样的文件或目录

c - 如何更改此队列顺序?

c - C中的分词器程序