简体   繁体   English

C 程序 -- 使用 scanf 的 While 循环 function 即使不满足条件,条件也会运行。 如何解决这个问题?

[英]C Program -- While loop with scanf function condition runs even if condition is not met. How to fix this?

I am attempting to create a program where you input 2 numbers and then print out the first number to the power of the second number.我正在尝试创建一个程序,您可以在其中输入 2 个数字,然后打印出第一个数字的第二个数字的幂。

I tried using a while loop to say that if the numbers you input are two, then you keep repeating the program, otherwise if you input more than 2, you end the loop and print out that you input too many numbers.我试着用一个 while 循环来表示如果你输入的数字是两个,那么你就一直重复这个程序,否则如果你输入超过 2 个,你就结束循环并打印出你输入了太多的数字。

However, the code still works if I input more than two, and I assume this is because the scanf function ignores anything other than the first two numbers I input.但是,如果我输入两个以上,代码仍然有效,我认为这是因为scanf function 忽略了我输入的前两个数字以外的任何内容。

How do I fix this program so that it works as I had intended?我该如何修复该程序,使其按我的预期运行?

#include <stdio.h>

#include <math.h>

int main(void)

{

    float x, exp;

    printf("Please enter a number followed by the power ");
    printf("you want to raise it to: ");
    

    while(scanf("%f%f", &x, &exp) == 2)
    {
        printf("%f\n", pow(x, exp));
        printf("Enter the next pair of numbers:\n");
    }
    
    printf("You entered too many numbers!\n");

    return 0;
}

User input is tricky.用户输入很棘手。 Get input as a string, and loop on that.以字符串形式获取输入,然后对其进行循环。 Just keep in mind that the user may enter each input one at a time.请记住,用户可以一次输入一个输入。 Either require it to be correct (user types two numbers followed by Enter) or take effort to handle multiple correct inputs (user types one number followed by Enter and then another number followed by Enter).要么要求它是正确的(用户键入两个数字,然后按 Enter),要么努力处理多个正确的输入(用户键入一个数字,然后按 Enter,然后输入另一个数字,然后按 Enter)。 Here we will require both inputs on the same line:在这里,我们将要求两个输入都在同一行:

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

int main(void)
{
    float x, exp;

    printf("Please enter a number followed by the power ");
    printf("you want to raise it to: ");

    char s[1000];
    while (fgets(s, sizeof(s), stdin))
    {
        if (sscanf(s, "%f %f", &x, &exp) != 2)
        {
            puts("Invalid input, my dude.");
            break;  // Stop running if user supplies fewer than two valid inputs
        }
        else
        {
            printf("%f\n", pow(x, exp));
        }
        printf("Enter the next pair of numbers:\n");
    }

    return 0;
}

This requires the user to terminate the stream to quit, BTW, by pressing ^Z , Enter on Windows or ^D on Linux. You could easily add additional methods to terminate in the loop (for example, terminate if s is empty or sscanf returns 0 ), but this is not necessary.这需要用户终止 stream 以退出,顺便说一句,通过按^Z ,在 Windows 上Enter或在 Linux 上输入^D 。您可以轻松添加其他方法来终止循环(例如,如果s为空或sscanf返回则终止0 ),但这不是必需的。

EDIT: There are other issues too.编辑:还有其他问题。 For example, what if the user enters more than two inputs on a line.例如,如果用户在一行中输入了两个以上的输入怎么办。 Should I detect that?我应该检测到吗? Again, for programs like this, it is ok to assume that inputs will be valid unless your assignment specifically requires you to detect error conditions.同样,对于这样的程序,假设输入有效是可以的,除非您的作业特别要求您检测错误条件。

EDIT 2: If you wish to catch a more than two items entered error, you must make sure that sscanf() consumed the entire line.编辑 2:如果您希望捕获超过两个项目的输入错误,您必须确保sscanf()消耗了整行。 Fortunately there is an easy way to do that.幸运的是,有一种简单的方法可以做到这一点。 Change line 15 to:将第 15 行更改为:

        int n;
        if ((sscanf(s, "%f %f %n", &x, &exp, &n) != 2) || (s[n] != '\0'))

What that does is skip all whitespace after the second float to either end of string or the next available item in the string and returns the index of that position in n .它的作用是跳过第二个浮点数之后的所有空格到字符串的任一端或字符串中的下一个可用项,并返回n中 position 的索引。

After that we only need to verify that the end of string condition is what was found.之后我们只需要验证字符串结束条件是否是找到的。

If the user types more than two numbers this will not be an error.如果用户键入两个以上的数字,这不会出错。 They will be stored in the input buffer and read in the next call of scanf.它们将存储在输入缓冲区中,并在下一次调用 scanf 时读取。

Pay attention to that the user can type two numbers on the same line or in different lines.请注意,用户可以在同一行或不同行中键入两个数字。

In fact you can not prevent the user to enter on one line numerous numbers.事实上,您无法阻止用户在一行中输入大量数字。 But you can check that at most two lines there are entered two numbers.但是您可以检查最多有两行输入了两个数字。

So you need to split the input.所以你需要拆分输入。

The first number will be read using scanf and the second number will be read using fgtes .第一个数字将使用scanf读取,第二个数字将使用fgtes读取。

Here is a demonstration program.这是一个演示程序。

#include <stdio.h>
#include <math.h>

int main(void) 
{
    printf("Please enter a number followed by the power ");
    printf("you want to raise it to: ");
    
    while ( 1 )
    {
        float x, exp;

        if ( scanf( "%f ", &x ) != 1 ) break;
        
        char s[20];
        
        if ( !fgets( s, sizeof( s ), stdin ) ) break;
        
        int n;
        
        if ( sscanf( s, "%f %n", &exp, &n ) != 1 || s[n] != '\0' ) break;

        printf("%f\n", pow(x, exp));
        printf("Enter the next pair of numbers: ");
    }
    
    puts( "You entered too many or too few numbers!" );    

    
    return 0;
}

Its output might look like它的 output 可能看起来像

Please enter a number followed by the power you want to raise it to: 1 2
1.000000
Enter the next pair of numbers: 2
3
8.000000
Enter the next pair of numbers: 4
5 6
You entered too many or too few numbers!

Simply put, your code will always continue.简而言之,您的代码将始终继续。 This is just because of how scanf works:这只是因为 scanf 的工作原理:

Scanf scans input from stdin. Scanf 扫描来自标准输入的输入。 When scanf reaches the end of stdin and still hasn't scanned everything it expected to scan, it waits until the user sends a newline (presses enter).当 scanf 到达 stdin 的末尾并且仍然没有扫描它期望扫描的所有内容时,它会等待直到用户发送换行符(按回车键)。 In other words, so long as you enter valid floats, your scanf will never return a value lower than the expected float count .换句话说,只要您输入有效的浮点数,您的 scanf 就永远不会返回低于预期浮点数的值

On the other end, once scanf is finished with scanning stdin, it immediately evaluates the variables and returns.另一方面,一旦 scanf 完成扫描标准输入,它会立即评估变量并返回。 This means that there is still some input left in stdin that has not yet been read.这意味着标准输入中仍有一些输入尚未被读取。 In fact, when scanf next runs, it will resume scanning exactly where it left off .事实上,当 scanf 下次运行时,它会从中断的地方继续扫描。 Take this sample code:拿这个示例代码:

int main()
{
    int x,y;
    int ct = scanf("%d%d",&x,&y);
    printf("%d (%d,%d)\n",ct,x,y);
    scanf("%d",&x);
    printf("%d\n",x);
}

If you compile and run this, try inputting three ints at once.如果编译并运行它,请尝试一次输入三个整数。 The second scanf will immediately terminate because it is reading the third integer that was inputted.第二个 scanf 将立即终止,因为它正在读取输入的第三个 integer。

If you are trying to get a specific number of inputs, I would suggest scanning the user's input as a string and then using sscanf (scanf for strings).如果您正在尝试获取特定数量的输入,我建议将用户输入扫描为字符串,然后使用 sscanf(对字符串扫描)。 You could also check for the number of spaces in the string, then, to determine if there are too many inputs.您还可以检查字符串中的空格数,然后确定输入是否过多。 If you want to get a little tricky, you could continue to use scanf but then check whether bytes are remaining in stdin before you continue.如果您想变得有点棘手,您可以继续使用 scanf 但在继续之前检查标准输入中是否剩余字节。 Here is a good answer that will help if you want to keep using scanf as is, but checking whether stdin is empty.如果您想按原样继续使用 scanf 但检查 stdin 是否为空,是一个很好的答案,它会有所帮助。

There is another issue with your code though;但是您的代码还有另一个问题; what happens when a user inputs something other than a float?当用户输入浮点数以外的内容时会发生什么? But that is a different question entirely (and one where my personal suggestion would be to analyze the entire scanned string).但这完全是一个不同的问题(我个人的建议是分析整个扫描的字符串)。

The problem with using scanf is that it treats all whitespace characters (eg spaces and newline characters) as equal.使用scanf的问题在于它将所有空白字符(例如空格和换行符)视为相同。 For example, scanf won't care whether the numbers you entered are on the same line or not.例如, scanf不会关心你输入的数字是否在同一行。

If scanf is asked to read two numbers, but the user instead enters three numbers on the same line, then your first call to scanf will only read the first two numbers and leave the third number on the input stream, so that the next scanf call in the next loop iteration will read it as the next first number.如果要求scanf读取两个数字,但用户却在同一行输入了三个数字,那么您对scanf的第一次调用将只读取前两个数字,而将第三个数字保留在输入 stream 上,以便下一次scanf调用在下一个循环迭代中会将其读取为下一个第一个数字。 This is not what you want.这不是你想要的。

Therefore, for line-based user input, it is probably better not to use scanf .因此,对于基于行的用户输入,最好不要使用scanf Instead, it makes more sense to always read exactly one line per loop iteration.相反,每次循环迭代始终只读取一行更有意义。 You can do this with the function fgets .您可以使用 function fgets执行此操作。

After using fgets to read a line of input, two of the other answers use sscanf to convert both numbers at once.在使用fgets读取一行输入后,其他两个答案使用sscanf一次转换两个数字。 However, you can also convert one number at a time using strtof :但是,您也可以使用strtof一次转换一个数字:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <stdbool.h>

int main(void)
{
    //we want to provide the user with a different prompt the first time,
    //so we must remember whether it is the first time
    bool first = true;

    //infinite loop
    while ( true )
    {
        float x, exp;
        char line[100];
        char *p, *q;

        //prompt user for input
        if ( first )
        {
            printf(
                "Please enter a number followed by the power "
                "you want to raise it to: "
            );

            //remember to use a different prompt next time
            first = false;
        }
        else
        {
            printf("Enter the next pair of numbers: ");
        }

        //attempt to read one line of input
        if ( fgets( line, sizeof line, stdin ) == NULL )
        {
            //break out of infinite loop
            break;
        }

        //attempt to find newline character
        p = strchr( line, '\n' );

        //make sure entire line was read in
        if ( p == NULL && !feof(stdin) )
        {
            //only accept missing newline character on end-of-file
            if ( !feof(stdin) )
            {
                int c;

                printf( "Line too long for input buffer!\n" );

                //discard remainder of line
                do
                {
                    c = getchar();

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

                continue;
            }
        }
        else
        {
            //remove newline character by overwriting it with null character
            *p = '\0';
        }

        //start parsing at start of line
        p = line;

        //attempt to convert first number
        x = strtof( p, &q );

        //determine whether conversion of first number succeeded
        if ( p == q )
        {
            printf( "Conversion of first number failed!\n" );
            continue;
        }

        //continue parsing at end of first number
        p = q;

        //attempt to convert second number
        exp = strtof( p, &q );

        //determine whether conversion of second number succeeded
        if ( p == q )
        {
            printf( "Conversion of second number failed!\n" );
            continue;
        }

        //verify that remainder of line is either empty or only
        //consists of whitespace characters
        for ( p = q; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "Unexpected character found after second number!\n" );

                //we cannot use the "continue" keyword here, because
                //we want to continue to the next iteration of the
                //outer loop, not the inner loop
                goto continue_outer_loop;
            }
        }

        //print result
        printf( "Input accepted, the result is: %f\n", pow(x, exp) );

    continue_outer_loop:
        continue;
    }

    return 0;
}

This program has the following behavior:该程序具有以下行为:

Please enter a number followed by the power you want to raise it to: abc
Conversion of first number failed!
Enter the next pair of numbers: 10
Conversion of second number failed!
Enter the next pair of numbers: 10 abc
Conversion of second number failed!
Enter the next pair of numbers: 10 20
Input accepted, the result is: 100000000000000000000.000000
Enter the next pair of numbers: 10 20 30
Unexpected character found after second number!

As you can see, the program correctly rejects the input if it contains a third number.如您所见,如果输入包含第三个数字,程序会正确地拒绝该输入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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