C语言 无法清除缓冲区

fquxozlt  于 2023-10-16  发布在  其他
关注(0)|答案(2)|浏览(128)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

void perimeter(double, double, double, double *, double *);
double square(double, double, double, double);
void heights(double, double, double, double *, double *, double *, double);
void medians(double, double, double, double *, double *, double *);
void bisectors(double, double, double, double *, double *, double *, double);

int main()
{
    char buffer[32];
    double first_side = 0;
    double second_side = 0;
    double third_side = 0;
    
    double per1;
    double per2;
    
    double height_to_firstside;
    double height_to_secondside;
    double height_to_thirdside;
    
    double median_to_firstside;
    double median_to_secondside;
    double median_to_thirdside;
    
    double bisector_to_firstside;
    double bisector_to_secondside;
    double bisector_to_thirdside;
    
    for (int i = 0; i < 3; i++)
    {
        printf("Enter a number for side %d: ", i + 1);
        fgets(buffer, sizeof(buffer), stdin); // Зчитуємо рядок
        
        //Validation
        if (strspn(buffer, "0123456789.") != strlen(buffer) - 1) 
        {
            printf("Invalid input. Please enter other nums.\n");
            return 0;
        }
        
        int dot_count = 0;
        for (int j = 0; j < strlen(buffer); j++)
        {
            if (buffer[0] == '0' && buffer[1] != '.')
            {
                printf("Invalid input. Please enter other nums.\n");
                return 0;
            }
            
            if (buffer[j] == '.')
            {
                dot_count++;
            }
            if (dot_count >= 2)
            {
                printf("Invalid input. Please enter other nums.\n");
                return 0;
            }
        }
        
        double input;
        sscanf(buffer, "%lf", &input);

        if (i == 0) // Зберігаємо значення в відповідній змінній
        {
            first_side = input;
        }
        else if (i == 1)
        {
            second_side = input;
        }
        else if (i == 2)
        {
            third_side = input;
        }
        /*int c;
        while ((c = getchar()) != '\n' && c != EOF) {}
        memset(buffer, 0, sizeof(buffer));*/
    }

    if ((first_side + second_side) < third_side || (second_side + third_side) < first_side || (first_side + third_side) < second_side)
    {
        printf("Unreal triangle, enter other nums\n");
        return 0;
    }

    perimeter(first_side, second_side, third_side, &per1, &per2);
    double area = square(first_side, second_side, third_side, per2);
    heights(first_side, second_side, third_side, &height_to_firstside, &height_to_secondside, &height_to_thirdside, per2);
    medians(first_side, second_side, third_side, &median_to_firstside, &median_to_secondside, &median_to_thirdside);
    bisectors(first_side, second_side, third_side, &bisector_to_firstside, &bisector_to_secondside, &bisector_to_thirdside, per2);

    //results
    printf("Perimeter: %lf\n", per1);
    printf("Semi-perimeter: %lf\n", per2);
    
    printf("Square: %lf\n", area);
    
    printf("Height for the first side: %lf\n", height_to_firstside);
    printf("Height for the second side: %lf\n", height_to_secondside);
    printf("Height for the third side: %lf\n", height_to_thirdside);
    
    printf("Median for the first side: %lf\n", median_to_firstside);
    printf("Median for the second side: %lf\n", median_to_secondside);
    printf("Median for the third side: %lf\n", median_to_thirdside);
    
    printf("Bisector for the first side: %lf\n", bisector_to_firstside);
    printf("Bisector for the second side: %lf\n", bisector_to_secondside);
    printf("Bisector for the third side: %lf\n", bisector_to_thirdside);

    return 0;
}

void perimeter(double first, double second, double third, double *pPer1, double *pPer2)
{
    *pPer1 = first + second + third;
    *pPer2 = *pPer1 / 2;
}

double square(double first, double second, double third, double semi_perimeter)
{
    double square_size;
    square_size = sqrt(semi_perimeter * (semi_perimeter - first) * (semi_perimeter - second) * (semi_perimeter - third));

    return square_size;
}

void heights(double first, double second, double third, double *pHeight_firstside, double *pHeight_secondside, double *pHeight_thirdside, double semi_perimeter)
{
    *pHeight_firstside = 2 * (sqrt(semi_perimeter * (semi_perimeter - first) * (semi_perimeter - second) * (semi_perimeter - third))) / first;
    *pHeight_secondside = 2 * (sqrt(semi_perimeter * (semi_perimeter - first) * (semi_perimeter - second) * (semi_perimeter - third))) / second;
    *pHeight_thirdside = 2 * (sqrt(semi_perimeter * (semi_perimeter - first) * (semi_perimeter - second) * (semi_perimeter - third))) / third;
}

void medians(double first, double second, double third, double *pMedian_firstside, double *pMedian_secondside, double *pMedian_thirdside)
{
    *pMedian_firstside = 0.5 * (sqrt((2 * second * second) + (2 * third * third) - (first * first)));
    *pMedian_secondside = 0.5 * (sqrt((2 * first * first) + (2 * third * third) - (second * second)));
    *pMedian_thirdside = 0.5 * (sqrt((2 * first * first) + (2 * second * second) - (third * third)));
}

void bisectors(double first, double second, double third, double *pBisector_firstside, double *pBisector_secondside, double *pBisector_thirdside, double semi_perimeter)
{
    *pBisector_firstside = (2 / (second + third)) * (sqrt((second * third * semi_perimeter) * (semi_perimeter - first)));
    *pBisector_secondside = (2 / (first + third)) * (sqrt((first * third * semi_perimeter) * (semi_perimeter - second)));
    *pBisector_thirdside = (2 / (first + second)) * (sqrt((first * second * semi_perimeter) * (semi_perimeter - third)));
}

这是我的代码,正如你所看到的,我使用一个字符数组从用户那里获取值,然后验证它,最后将其转换为双精度型,但这一切都是在一个循环中完成的,所以我遇到了一个问题,我无法清除缓冲区,当用户输入第一个值时,然而,省略了接下来的两个,那么第一个值也被分配给了第二个和第三个。
我尝试使用fflush(stdin),但它不工作,我也尝试了这个:

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

但它也不起作用,所以我甚至不知道我现在应该尝试什么。

6ie5vjzr

6ie5vjzr1#

你当然可以清除输入缓冲区。或者只是像预期的那样消耗数据。
fflush未定义为输入。至少不是终端输入。从Linux man页面:

  • 对于与可查找文件(例如,磁盘文件,但不是管道或终端)相关联的输入流,fflush()将丢弃任何已从底层文件获取但尚未被应用程序使用的缓冲数据。

它只为输出而定义,并不意味着discard任何东西。例如:您可以让stdout的驱动程序等待\ntty上打印该行,但随后程序结束。缓冲区中的字节无论如何都会被打印出来:OS在结束时刷新输出缓冲区。如果在代码中需要立即真正打印数据,则有fflush
一些C编译器接受fflush作为输入流的扩展,AFAIK仅供学生使用。
Windows下,对于控制台输入,可以调用

BOOL WINAPI FlushConsoleInputBuffer(
  _In_ HANDLE hConsoleInput
);

如文档中所述。在Linux上,我认为您可以搜索ioctl调用或termios来执行相同的操作。

只消耗所有输入数据

如果你消耗了所有的数据,那么输入缓冲区就不是你的代码所关心的了。

我会给你一个例子,重新排列你的代码。

封装

围绕数据编写代码。只要有机会就使用封装。这不仅仅是一个OOP花哨的词。这是一个强大的东西。
从你的代码:

  • 数据是一个三角形
  • 用户输入该方的度量
  • 在确定输入的测量值对于三角形可行之后,代码将计算一些值
  • 将显示这些值
  • 程序结束。

程序中计算的值只有在有输入三角形时才有意义。如果一方的措施发生变化,所有值都无效。
所以...
想想这个:

typedef struct
{
    double   s1;
    double   s2;
    double   s3;
    Measures msrs;
} Triangle;

这是你的数据。至于msrs,从您的代码

typedef struct
{
    double perimeter;
    double semi_perimeter;
    double area;
    double height_to_firstside;
    double height_to_secondside;
    double height_to_thirdside;
    double median_to_firstside;
    double median_to_secondside;
    double median_to_thirdside;
    double bisector_to_firstside;
    double bisector_to_secondside;
    double bisector_to_thirdside;
} Measures;

使用Triangle的值INSIDE可以轻松地强制依赖关系。并且还可以在代码中处理任意数字或三角形。

如何创建Triangle

Triangle* so_create()
{
    Triangle* tri = malloc(sizeof(Triangle));
    if (tri == NULL) return NULL;
    printf(
        "    Triangle:\n\tPlease enter values for the 3 "
        "sides: ");
    int res =
        scanf("%lf %lf %lf", &tri->s1, &tri->s2, &tri->s3);
    if (res != 3)
    {
        free(tri);
        return NULL;
    }
    if (not so_is_triangle(tri))
    {
        free(tri);
        return NULL;
    }
    tri->msrs = so_get_measures(tri);
    return tri;
}

这是程序中main的起始代码。但在这里

  • scanf的单个调用读取3个度量
  • 在常见的C样式中,如果三角形的度量值可以,则so_is_triangle()返回1
  • 如果测量检查,则调用so_get_measures()计算所有三角形的参数

考虑这些函数:

Triangle* so_create();
    Triangle* so_destroy(Triangle*);
    int       so_is_triangle(Triangle*);
    Measures  so_get_measures(Triangle*);
    int       so_show_triangle(Triangle*, const char*);

有明显的意义。

main进行简单测试

int main(void)
{
    Triangle* one = so_create();
    if (one == NULL)
    {
        fprintf(
            stderr,
            "    Invalid data entered. Please try again\n");
        return -1;
    };
    so_show_triangle(one, "\n\n[First triangle]");
    so_destroy(one);
    return 0;
}

这样读起来更容易。

测试输出

Triangle:
        Please enter values for the 3 sides: 3 4 5

[First triangle]    Sides: [    3.0000,     4.0000,     5.0000]

    Perimeter:         12.0000
    Semi-perimeter:     6.0000
    Square:             6.0000

    Height for the first side:        4.0000
    Height for the second side:       3.0000
    Height for the third side:        2.4244

    Median for the first side:        4.2720
    Median for the second side:       3.6056
    Median for the third side:        2.5000

    Bisector for the first side:      4.2164
    Bisector for the second side:     3.3541
    Bisector for the third side:      2.4244

关于so_show_triangle

请注意,一个变量的12个printf比12个变量的单个printf慢数百倍。更难纠正或调整...
比较:

// results
    printf("Perimeter: %lf\n", per1);
    printf("Semi-perimeter: %lf\n", per2);

    printf("Square: %lf\n", area);

    printf(
        "Height for the first side: %lf\n",
        height_to_firstside);
    printf(
        "Height for the second side: %lf\n",
        height_to_secondside);
    printf(
        "Height for the third side: %lf\n",
        height_to_thirdside);

    printf(
        "Median for the first side: %lf\n",
        median_to_firstside);
    printf(
        "Median for the second side: %lf\n",
        median_to_secondside);
    printf(
        "Median for the third side: %lf\n",
        median_to_thirdside);

    printf(
        "Bisector for the first side: %lf\n",
        bisector_to_firstside);
    printf(
        "Bisector for the second side: %lf\n",
        bisector_to_secondside);
    printf(
        "Bisector for the third side: %lf\n",
        bisector_to_thirdside);

printf(
        "\
    Sides: [%10.4f, %10.4f, %10.4f]\n\
\n\
    Perimeter:      %10.4f\n\
    Semi-perimeter: %10.4f\n\
    Square:         %10.4f\n\
\n\
    Height for the first side:    %10.4f\n\
    Height for the second side:   %10.4f\n\
    Height for the third side:    %10.4f\n\
\n\
    Median for the first side:    %10.4f\n\
    Median for the second side:   %10.4f\n\
    Median for the third side:    %10.4f\n\
\n\
    Bisector for the first side:  %10.4f\n\
    Bisector for the second side: %10.4f\n\
    Bisector for the third side:  %10.4f\n",
        tri->s1, tri->s2, tri->s3,
        tri->msrs.perimeter, tri->msrs.semi_perimeter,
        tri->msrs.area, tri->msrs.height_to_firstside,
        tri->msrs.height_to_secondside,
        tri->msrs.bisector_to_thirdside,
        tri->msrs.median_to_firstside,
        tri->msrs.median_to_secondside,
        tri->msrs.median_to_thirdside,
        tri->msrs.bisector_to_firstside,
        tri->msrs.bisector_to_secondside,
        tri->msrs.bisector_to_thirdside);

在例子中使用。

示例C完整代码

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

typedef struct
{
    double perimeter;
    double semi_perimeter;
    double area;
    double height_to_firstside;
    double height_to_secondside;
    double height_to_thirdside;
    double median_to_firstside;
    double median_to_secondside;
    double median_to_thirdside;
    double bisector_to_firstside;
    double bisector_to_secondside;
    double bisector_to_thirdside;
} Measures;

typedef struct
{
    double   s1;
    double   s2;
    double   s3;
    Measures msrs;
} Triangle;

Triangle* so_create();
Triangle* so_destroy(Triangle*);
int       so_is_triangle(Triangle*);
Measures  so_get_measures(Triangle*);
int       so_show_triangle(Triangle*, const char*);

int main(void)
{
    Triangle* one = so_create();
    if (one == NULL)
    {
        fprintf(
            stderr,
            "    Invalid data entered. Please try again\n");
        return -1;
    };
    so_show_triangle(one, "\n\n[First triangle]");
    so_destroy(one);
    return 0;
}

Triangle* so_create()
{
    Triangle* tri = malloc(sizeof(Triangle));
    if (tri == NULL) return NULL;
    printf(
        "    Triangle:\n\tPlease enter values for the 3 "
        "sides: ");
    int res =
        scanf("%lf %lf %lf", &tri->s1, &tri->s2, &tri->s3);
    if (res != 3)
    {
        free(tri);
        return NULL;
    }
    if (not so_is_triangle(tri))
    {
        free(tri);
        return NULL;
    }
    tri->msrs = so_get_measures(tri);
    return tri;
}

Triangle* so_destroy(Triangle* tri)
{
    if (tri == NULL) return NULL;
    free(tri);
    return NULL;
}

int so_is_triangle(Triangle* tri)
{
    if (tri == NULL) return 0;
    if ((tri->s1 + tri->s2) < tri->s3 ||
        (tri->s2 + tri->s3) < tri->s1 ||
        (tri->s1 + tri->s3) < tri->s2)
        return 0;
    return 1;  // ok it is a triangle
}

Measures so_get_measures(Triangle* tri)
{
    Measures m = {0};
    if (tri == NULL) return m;
    // perimeter
    m.perimeter      = tri->s1 + tri->s2 + tri->s3;
    m.semi_perimeter = m.perimeter / 2;
    // area
    m.area = sqrt(
        m.semi_perimeter * (m.semi_perimeter - tri->s1) *
        (m.semi_perimeter - tri->s2) *
        (m.semi_perimeter - tri->s3));
    // heights
    m.height_to_firstside =
        2 *
        (sqrt(
            m.semi_perimeter *
            (m.semi_perimeter - tri->s1) *
            (m.semi_perimeter - tri->s2) *
            (m.semi_perimeter - tri->s3))) /
        tri->s1;
    m.height_to_secondside =
        2 *
        (sqrt(
            m.semi_perimeter *
            (m.semi_perimeter - tri->s1) *
            (m.semi_perimeter - tri->s2) *
            (m.semi_perimeter - tri->s3))) /
        tri->s2;
    m.height_to_thirdside =
        2 *
        (sqrt(
            m.semi_perimeter *
            (m.semi_perimeter - tri->s1) *
            (m.semi_perimeter - tri->s2) *
            (m.semi_perimeter - tri->s3))) /
        tri->s3;
    // medians
    m.median_to_firstside =
        0.5 *
        (sqrt(
            (2 * tri->s2 * tri->s2) +
            (2 * tri->s3 * tri->s3) - (tri->s1 * tri->s1)));
    m.median_to_secondside =
        0.5 *
        (sqrt(
            (2 * tri->s1 * tri->s1) +
            (2 * tri->s3 * tri->s3) - (tri->s2 * tri->s2)));
    m.median_to_thirdside =
        0.5 *
        (sqrt(
            (2 * tri->s1 * tri->s1) +
            (2 * tri->s2 * tri->s2) - (tri->s3 * tri->s3)));
    // bisectors
    m.bisector_to_firstside =
        (2 / (tri->s2 + tri->s3)) *
        (sqrt(
            (tri->s2 * tri->s3 * m.semi_perimeter) *
            (m.semi_perimeter - tri->s1)));
    m.bisector_to_secondside =
        (2 / (tri->s1 + tri->s3)) *
        (sqrt(
            (tri->s1 * tri->s3 * m.semi_perimeter) *
            (m.semi_perimeter - tri->s2)));
    m.bisector_to_thirdside =
        (2 / (tri->s1 + tri->s2)) *
        (sqrt(
            (tri->s1 * tri->s2 * m.semi_perimeter) *
            (m.semi_perimeter - tri->s3)));
    return m;
}

int so_show_triangle(Triangle* tri, const char* msg)
{
    if (tri == NULL) return -1;
    if (msg != NULL) printf("%s", msg);
    //
    printf(
        "\
    Sides: [%10.4f, %10.4f, %10.4f]\n\
\n\
    Perimeter:      %10.4f\n\
    Semi-perimeter: %10.4f\n\
    Square:         %10.4f\n\
\n\
    Height for the first side:    %10.4f\n\
    Height for the second side:   %10.4f\n\
    Height for the third side:    %10.4f\n\
\n\
    Median for the first side:    %10.4f\n\
    Median for the second side:   %10.4f\n\
    Median for the third side:    %10.4f\n\
\n\
    Bisector for the first side:  %10.4f\n\
    Bisector for the second side: %10.4f\n\
    Bisector for the third side:  %10.4f\n",
        tri->s1, tri->s2, tri->s3,
        tri->msrs.perimeter, tri->msrs.semi_perimeter,
        tri->msrs.area, tri->msrs.height_to_firstside,
        tri->msrs.height_to_secondside,
        tri->msrs.bisector_to_thirdside,
        tri->msrs.median_to_firstside,
        tri->msrs.median_to_secondside,
        tri->msrs.median_to_thirdside,
        tri->msrs.bisector_to_firstside,
        tri->msrs.bisector_to_secondside,
        tri->msrs.bisector_to_thirdside);

    return 0;
}

// https://stackoverflow.com/questions/77216624/impossibility-to-clear-the-buffer
kzmpq1sx

kzmpq1sx2#

虽然您已经勇敢地尝试验证用户的输入,但它很难阅读,而且似乎给您带来了问题。当涉及超过50行代码时,可能会发生这种情况。
下面显示了一个使用strtod()将ASCII数字转换为double的示例。这个库函数将作为第二个参数传递的指针设置为指向成功转换的浮点值之外。这段代码要求用户输入一个有效的正浮点数,也许加上前导空格,仅此而已。也许你会使用它并扩展它来满足你的需求:

void get_3posdbls( double *s1, double *s2, double *s3 ) {
    for( int i = 1; i <= 3; ) {
        double *dp = (i == 1) ? s1 : (i == 2) ? s2 : s3;

        char buffer[32];
        printf( "Enter a number for side %d: ", i );
        if( fgets( buffer, sizeof buffer, stdin ) == 0 )
            return;

        char *next;
        *dp = strtod( buffer, &next );

        if( *dp <= 0.0 || next == buffer || *next != '\n' )
            printf( "Invalid input. Please enter number only\n" );
        else
            i++;
    }
}

int main( void ) {
    for( ;; ) {
        double side1 = 0;
        double side2 = 0;
        double side3 = 0;

        get_3posdbls( &side1, &side2, &side3 );
        printf( "%5.2lf %5.2lf %5.2lf\n\n", side1, side2, side3 );
    }

    return 0;
}

结果如下:

Enter a number for side 1: 12.3
Enter a number for side 2: 45.6
Enter a number for side 3: 78.9
12.30 45.60 78.90

Enter a number for side 1: foobar
Invalid input. Please enter number only
Enter a number for side 1: -56.3 // negative values are rejected
Invalid input. Please enter number only
Enter a number for side 1: 35   // typed extra spaces after "35"
Invalid input. Please enter number only
Enter a number for side 1: 35
Enter a number for side 2: 36
Enter a number for side 3: 37
35.00 36.00 37.00

Enter a number for side 1:

编辑:

我真诚地写了《勇敢的尝试》。然而,通过尝试 * 过滤掉 * 坏字符串,代码将拒绝“3.14153e5”(314153.000),因为您可能没有意识到这是一个有效的浮点数表达式。花时间学习标准库函数并试验它们的使用通常会更好。
关于您的代码和这里显示的代码之间的差异的另一个注解。请注意,此函数中的代码仅在使用变量时定义变量。这种做法减少了在声明和第一次使用之间上下滚动的需要。

编辑(不是OP问题的一部分)

计算功能很好,隔离了不同的导出结果,但它们相当冗长,因此很难 * 扫描 * 为正确和完整。
下面是同一函数的两个版本:

// Version 1
void heights(double first, double second, double third, double *pHeight_firstside, double *pHeight_secondside, double *pHeight_thirdside, double semi_perimeter)
{
    *pHeight_firstside = 2 * (sqrt(semi_perimeter * (semi_perimeter - first) * (semi_perimeter - second) * (semi_perimeter - third))) / first;
    *pHeight_secondside = 2 * (sqrt(semi_perimeter * (semi_perimeter - first) * (semi_perimeter - second) * (semi_perimeter - third))) / second;
    *pHeight_thirdside = 2 * (sqrt(semi_perimeter * (semi_perimeter - first) * (semi_perimeter - second) * (semi_perimeter - third))) / third;
}

// Version 2
void heights(double s1, double s2, double s3, double *pH_s1, double *pH_s2, double *pH_s3, double sp)
{
    double numerator = 2 * sqrt(sp * (sp - s1) * (sp - s2) * (sp - s3));

    *pH_s1 = numerator / s1;
    *pH_s2 = numerator / s2;
    *pH_s3 = numerator / s3;
}

更少(正确)的代码是更好的代码。

相关问题