如何使用fgets()多次安全地处理用户输入 [英] How to use fgets() to safely handle user input more than once

查看:122
本文介绍了如何使用fgets()多次安全地处理用户输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

很抱歉,如果我重复,但是我已经尝试了一切,而且我不知道为什么这段代码一直在中断.最高优先级的目标是使此代码安全地处理输入,或使用户可以在控制台中键入的任何内容都不会损坏它.但是,我还需要它能够多次运行.fgets()不允许我这样做,因为它会在某处持续读取'\ n'并阻止我在do/while循环结束时多次输入输入.我已经尝试过冲洗标准输入法,我已经尝试过 scanf(%d * [^ \ n]"); 和普通的 scanf(%d * [^ \ n]");,但是这些都不起作用,实际上,它们破坏了代码!我使用网站来尝试获取安全处理输入"代码上班,但我不完全了解他们在做什么.我尽了最大的努力尝试拼写(拼写?),但是我不确定是否做得对.我错过了什么?我不认为这个看似简单的问题会让人头疼!> _<

I'm sorry if I duplicate, but I have tried EVERYTHING, and I can't figure out why this code keeps breaking. The highest-priority goal was to make this code handle input safely, or just anything that the user can type into the console, without it breaking. However, I also need it to be able to run more than once. fgets() won't let me do that since it keeps reading '\n' somewhere and preventing me from entering input more than once when it hits the end of the do/while loop. I have tried fflushing stdin, I have tried scanf("%d *[^\n]"); and just regular scanf("%d *[^\n]");, but none of those work, and, in fact, they break the code! I used this website to try to get the "Safely handling input" code to work, but I don't completely understand what they're doing. I tried to jerry-rig (spelling?) it as best I could, but I'm not sure if I did it right. Did I miss something? I didn't think a problem this seemingly simple could be so much of a headache! >_<

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

using namespace std;

#define BUF_LEN 100
#define SPACE 32
#define SPCL_CHAR1F 33
#define SPCL_CHAR1L 47
#define SPCL_CHAR2F 58
#define SPCL_CHAR2L 64
#define SPCL_CHAR3F 91
#define SPCL_CHAR3L 96
#define NUMF  48
#define NUML 57
#define UC_CHARF 65
#define UC_CHARL 90
#define LC_CHARF 97
#define LC_CHARL 122

void main ()
{
    char* buffer;
    int SpcCounter=0, SpclCounter=0, NumCounter=0,LcCounter=0, UcCounter=0;
    char line[BUF_LEN],response[4];
    char*input="";
    bool repeat=false;

    do
    {
        for(int i=0;i<BUF_LEN;i++)
        {
            line[i]=NULL;
        }
        buffer=NULL;
        printf("Enter your mess of characters.\n");

        buffer=fgets(line,BUF_LEN,stdin);

        //To handle going over the buffer limit: BROKEN
        if(buffer!=NULL)
        {
            size_t last=strlen(line)-1;

            if(line[last]=='\n')

                line[last]='\0';
            else
            {
                fscanf(stdin,"%c *[^\n]");
            }
        }


        for(int i=0;i<BUF_LEN;i++)
        {
            char temp=buffer[i];
            if(temp==SPACE||temp==255)
                SpcCounter++;
            else if((temp >= SPCL_CHAR1F && temp <= SPCL_CHAR1L)||/*Special  characters*/
                (temp >= SPCL_CHAR2F && temp <= SPCL_CHAR2L)||
                (temp >= SPCL_CHAR3F && temp <= SPCL_CHAR3L))
                SpclCounter++;
            else if (temp >=NUMF && temp <= NUML)/*Numbers*/
                NumCounter++;
            else if (temp >= UC_CHARF && temp <= UC_CHARL)/*Uppercase letters*/
                UcCounter++;
            else if (temp >= LC_CHARF && temp <= LC_CHARL)/*Lowercase letters*/
                LcCounter++;
        }

        printf("There were %i space%s, %i special character%s, %i number%s, and %i letter%s,\n"
            "consisting of %i uppercase letter%s and %i lowercase.\n",
            SpcCounter,(SpcCounter==1?"":"s"),SpclCounter,(SpclCounter==1?"":"s"), NumCounter,(NumCounter==1?"":"s"),UcCounter+LcCounter,
            (UcCounter+LcCounter==1?"":"s"), UcCounter,(UcCounter==1?"":"s"), LcCounter);
        printf("Would you like to do this again? (yes/no)");

        input=fgets(response,4,stdin);
        /*
        ALL BROKEN
        if(input!=NULL)
        {
            size_t last=strlen(response)-1;

            if(response[last]=='\n')
                response[last]='\0';
            else
            {
                fscanf(stdin,"%*[^\n]");
                fscanf(stdin,"%c");
            }
        }
        */

        //To capitalize the letters
        for(int i=0;i<4;i++)
        {
            char* temp=&response[i];
            if (*temp >= LC_CHARF && *temp <= LC_CHARL)
                *temp=toupper(*temp);//Capitalize it
        }
        //To set repeat: WORKS, BUT WEIRD
        repeat=!strncmp(input,"YES",4);
    }
    while(repeat);
}

推荐答案

为了在C语言(如果使用的是C风格的字符串,在C ++中)中提供安全可靠的用户输入,我通常会恢复到我的最喜欢的语言, getLine 函数:

For safe, secure user input in C (and in C++ if I'm using C-style strings), I usually revert to an old favorite of mine, the getLine function:

// Use stdio.h and string.h for C.
#include <cstdio>
#include <cstring>

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Output prompt then get line with buffer overrun protection.
    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.
    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[strlen(buff)-1] = '\0';
    return OK;
}

此功能:

  • 可以根据需要输出提示.
  • 使用 fgets 以避免缓冲区溢出.
  • 在输入期间检测文件结束.
  • 通过检测结尾是否缺少换行符来检测行是否太长.
  • 删除换行符.
  • 吃掉"字符直到下一个换行符,以确保在下次调用此函数时不会将它们留在输入流中.

这是一段相当扎实的代码,已经在许多年内进行了测试,并且是解决用户输入问题的好方法.

It's a fairly solid piece of code that's been tested over many years and is a good solution to the problem of user input.

就您为问题目的使用的方式而言,我将添加一些与您所拥有的功能非常相似的东西,但是使用 getLine 函数,而不是直接调用 fgets 并摆弄结果.首先是一些标题和相同的定义:

In terms of how you call it for the purposes in your question, I would add something very similar to what you have, but using the getLine function instead of directly calling fgets and fiddling with the results. First some headers and the same definitions:

#include <iostream>
#include <cstdlib>
#include <cctype>

#define BUF_LEN 100
#define SPACE 32
#define SPCL_CHAR1F 33
#define SPCL_CHAR1L 47
#define SPCL_CHAR2F 58
#define SPCL_CHAR2L 64
#define SPCL_CHAR3F 91
#define SPCL_CHAR3L 96
#define NUMF 48
#define NUML 57
#define UC_CHARF 65
#define UC_CHARL 90
#define LC_CHARF 97
#define LC_CHARL 122

然后 main 的第一部分(使用函数)收集有效行以进行评估:

Then the first part of main gathering a valid line (using the function) to be evaluated:

int main () {
    int SpcCounter, SpclCounter, NumCounter, LcCounter, UcCounter;
    char line[BUF_LEN], response[4];
    bool repeat = false;

    do {
        SpcCounter = SpclCounter = NumCounter = LcCounter = UcCounter = 0;

        // Get a line until valid.

        int stat = getLine ("\nEnter a line: ", line, BUF_LEN);
        while (stat != OK) {
            // End of file means no more data possible.

            if (stat == NO_INPUT) {
                cout << "\nEnd of file reached.\n";
                return 1;
            }

            // Only other possibility is "Too much data on line", try again.

            stat = getLine ("Input too long.\nEnter a line: ", line, BUF_LEN);
        }

请注意,我更改了将计数器设置为零的位置.您的方法是让它们每次通过循环累加值,而不是将每条输入线的值都重置为零.接下来是您自己的代码,该代码将每个字符分配给一个类:

Note that I've changed where the counters are set to zero. Your method had them accumulating values every time through the loop rather than resetting them to zero for each input line. This is followed by your own code which assigns each character to a class:

        for (int i = 0; i < strlen (line); i++) {
            char temp=line[i];
            if(temp==SPACE||temp==255)
                SpcCounter++;
            else if((temp >= SPCL_CHAR1F && temp <= SPCL_CHAR1L)||
                (temp >= SPCL_CHAR2F && temp <= SPCL_CHAR2L)||
                (temp >= SPCL_CHAR3F && temp <= SPCL_CHAR3L))
                SpclCounter++;
            else if (temp >=NUMF && temp <= NUML)
                NumCounter++;
            else if (temp >= UC_CHARF && temp <= UC_CHARL)
                UcCounter++;
            else if (temp >= LC_CHARF && temp <= LC_CHARL)
                LcCounter++;
        }

        printf("There were %i space%s, %i special character%s, "
               "%i number%s, and %i letter%s,\n"
               "consisting of %i uppercase letter%s and "
               "%i lowercase.\n",
            SpcCounter,  (SpcCounter==1?"":"s"),
            SpclCounter, (SpclCounter==1?"":"s"),
            NumCounter, (NumCounter==1?"":"s"),
            UcCounter+LcCounter, (UcCounter+LcCounter==1?"":"s"),
            UcCounter, (UcCounter==1?"":"s"),
            LcCounter);

然后,最后,以与上述类似的方式询问用户是否要继续.

Then finally, a similar way as above for asking whether user wants to continue.

        // Get a line until valid yes/no, force entry initially.

        *line = 'x';
        while ((*line != 'y') && (*line != 'n')) {
            stat = getLine ("Try another line (yes/no): ", line, BUF_LEN);

            // End of file means no more data possible.

            if (stat == NO_INPUT) {
                cout << "\nEnd of file reached, assuming no.\n";
                strcpy (line, "no");
            }

            // "Too much data on line" means try again.

            if (stat == TOO_LONG) {
                cout << "Line too long.\n";
                *line = 'x';
                continue;
            }

            // Must be okay: first char not 'y' or 'n', try again.

            *line = tolower (*line);
            if ((*line != 'y') && (*line != 'n'))
                cout << "Line doesn't start with y/n.\n";
        }
    } while (*line == 'y');
}   


这样,您就可以基于可靠的输入例程(希望您可以将其理解为一个独立的单元)来构建程序逻辑.


That way, you build up your program logic based on a solid input routine (which hopefully you'll understand as a separate unit).

您可以通过删除显式范围检查并在 cctype()中使用适当的字符类来进一步改进代码,例如 isalpha() isspace().这将使它更易于移植(对于非ASCII系统),但我将在以后保留它作为练习.

You could further improve the code by removing the explicit range checks and using proper character classes with cctype(), like isalpha() or isspace(). That would make it more portable (to non-ASCII systems) but I'll leave that as an exercise for later.

该程序的示例运行是:

Enter a line: Hello, my name is Pax and I am 927 years old!
There were 10 spaces, 2 special characters, 3 numbers, and 30 letters,
consisting of 3 uppercase letters and 27 lowercase.
Try another line (yes/no): yes

Enter a line: Bye for now
There were 2 spaces, 0 special characters, 0 numbers, and 9 letters,
consisting of 1 uppercase letter and 8 lowercase.
Try another line (yes/no): no

这篇关于如何使用fgets()多次安全地处理用户输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆