关于缓冲区输入的若干问题

关于缓冲区输入的若干问题

前置

fgets()函数

  • 函数原型

char *fgets(char *str, int n, FILE *stream)

可以理解为(char *fgets(“容器的地址”, “容器的大小”, “从哪里读取”))

  • 一些需要注意的细节
  1. fgets()函数的眼里,换行符’\n’也是它要读取的一个普通字符而已。在读取键盘输入的时候会把最后输入的回车符也存进数组里面,即会把’\n’也存进数组里面,而又由于字符串本身会是以’\0’结尾的。所以在输入字符个数没有超过第二个参数指定大小之前,你输入n个字符按下回车输入,fgets()存储进第一个参数指定内存地址的是n+2个字节。最后面会多出一个’\n’和一个’\0’,而且’\n’是在’\0’的前面一个(\n\0)。

参考资料:fgets()函数的详解-使用技巧-C语言基础_fgets函数用法-CSDN博客


例题

简易计算器

来源:61CDay04

编写一个程序实现一个简单的计算器,能够处理加法、减法、乘法和除法。

输入:用户输入两个数字**(可以带小数)**和一个操作符(+、-、*、/)。

输出:显示计算的表达式及结果。

注意以下几个要求:

  1. 代码可以直接写在main函数里,不需要提取函数。
  2. 输入的表达式应当符合格式要求,应当正常录入两个操作数和一个运算符,否则直接退出程序。
  3. 在进行除法操作时,需要保证除数不为0,若除数为0,则直接退出程序。

运算结果截图如下:
在这里插入图片描述

提示:考虑switch结构,选择不同的运算符。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void) {
    char operator;
    double num1, num2;
    printf("请输入需要计算的表达式:");
    scanf("%lf %c %lf", &num1, &operator,&num2);

    switch (operator) {
    case '+':
        printf("计算的结果是:%lf + %lf = %.2lf\n", num1, num2, num1 + num2);
        break;
    case '-':
        printf("计算的结果是:%lf - %lf = %.2lf\n", num1, num2, num1 - num2);
        break;
    case '*':
        printf("计算的结果是:%lf * %lf = %.2lf\n", num1, num2, num1 * num2);
        break;
    case '/':
        if (0 == num2) {
            printf("Error:除数为0!\n");
        }else {
            printf("计算的结果是:%lf * /lf = %.2lf\n", num1, num2, num1 / num2);
        }
        break;
    }

    return 0;
}
简易计算器问题(一)

来源:61CDay04

在上面简单计算器题目的基础上,实现以下功能:

1.在进行完一次运算后,询问用户是否继续运算。只要用户输入y/Y就可以继续运算,而不是只能计算一次。

2.当用户的输入有误时,不退出程序,而是要求用户继续输入表达式计算。

参考的程序运行效果图如下:
在这里插入图片描述

提示:

可以考虑使用do…while循环结构,配合循环控制变量来解决,代码仍然都写在main函数中即可。

写完代码后,可以测试一下:在询问循环是否继续时输入"yyyyy",会发生什么情况呢?如何解决?

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void) {
    char enter[100];

    do {
        char operator;
        double num1, num2;
        
        printf("请输入需要计算的表达式:");
        int ret = scanf("%lf %c %lf", &num1, &operator,&num2);
        if (3 != ret) {
            printf("Error: 输入的表达式格式不合法!请重新输入。\n\n");
            while (getchar() != '\n') // 清空缓冲区至行尾,避免影响下次输入
                ;
            continue;                       // 继续下一次循环
        }
        /*while (getchar() != '\n') -- 这个循环会读取输入缓冲区中的字符,直到遇到换行符 '\n' 为止。getchar() 函数从标准输入读取一个字符,当读取到换行符时,循环结束。
                                            ;  -- 这个分号表示空语句,意味着循环体内没有需要执行的操作。循环的唯一目的是读取并丢弃字符。
                                continue; -- 这个语句表示跳过本次循环的剩余部分,立即开始下一次循环。*/


        switch (operator) {
        case '+':
            printf("计算的结果是:%lf + %lf = %.2lf\n", num1, num2, num1 + num2);
            break;
        case '-':
            printf("计算的结果是:%lf - %lf = %.2lf\n", num1, num2, num1 - num2);
            break;
        case '*':
            printf("计算的结果是:%lf * %lf = %.2lf\n", num1, num2, num1 * num2);
            break;
        case '/':
            if (0 == num2) {
                printf("Error:输入的表达式格式不合法!请重新输入!\n");
            }
            else {
                printf("计算的结果是:%lf * /lf = %.2lf\n", num1, num2, num1 / num2);
            }
            break;
        }

        //包含对输入“yyyyyyy”这种特殊情况的处理
        printf("是否继续计算?(输入‘y/Y’继续,其他任意键结束):");
        while (getchar() != '\n'){   //处理缓冲区\n (因为第一次scanf读取num1,operator,num2后,字符输入缓冲区中可能仍然包含前一次输入时按下的回车键 \n)
            ;
        }
        
        fgets(enter, sizeof(enter), stdin); // 使用 fgets 读取整行输入
        //下面检查输入的所有字符
        int i = 0, valid = 1;
        while (enter[i] != '\n') {
            if ('y' != enter[i] && 'Y' != enter[i]) {
                valid = 0;//valid=0说明do-while程序要退出
                break;
            }
            i++;
        }

        if (!valid) {
            break;//退出do-while大循环
        }

        printf("\n");//调整格式,无实际含义
    } while (1);

    return 0;
}
   
简易计算器问题(二)

来源:61CDay05

实现一个终端交互式的简易计算器,交互的形式大体如下:

在这里插入图片描述

要求至少提供四种运算加减乘除,如下:

int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
float divide(int a, int b);

并且在结束进程时,打印总共执行操作的次数。(也就是这些函数调用的次数)

注意:除法的实现,要求判断除数不为0,并且在除数为0时使用exit表示异常退出进程。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}
int multiply(int a, int b) {
    return a * b;
}

float divide(int a, int b) {
    return a / b;
}

int main(void) {
    char enter[100];
    int count = 0;

    do {
        char operator;
        int num1, num2;

        printf("请输入需要计算的表达式:");
        int ret = scanf("%d %c %d", &num1, &operator,&num2);
        if (3 != ret) {
            printf("Error: 输入的表达式格式不合法!请重新输入。\n\n");
            while (getchar() != '\n')		// 清空缓冲区至行尾,避免影响下次输入
                ;
            continue;                       // 继续下一次循环
        }
        /*while (getchar() != '\n') -- 这个循环会读取输入缓冲区中的字符,直到遇到换行符 '\n' 为止。getchar() 函数从标准输入读取一个字符,当读取到换行符时,循环结束。
                                ;  -- 这个分号表示空语句,意味着循环体内没有需要执行的操作。循环的唯一目的是读取并丢弃字符。
                         continue; -- 这个语句表示跳过本次循环的剩余部分,立即开始下一次循环。*/


        switch (operator) {
        case '+':
            printf("结果:%d\n",add(num1,num2));
            count++;
            break;
        case '-':
            printf("结果:%d\n", subtract(num1, num2));
            count++;
            break;
        case '*':
            printf("结果:%d\n", multiply(num1, num2));
            count++;
            break;
        case '/':
            if (0 == num2) {
                printf("Error:除数不可以为0\n");
                printf("\n总共执行的操作次数为:%d次\n", count);
                exit(1);
            }
            else {
                printf("结果:%d\n", divide(num1, num2));
                count++;
            }
            break;
        default:
            printf("无效的运算符\n");
        }

        //包含对输入“yyyyyyy”这种特殊情况的处理
        printf("是否继续计算?(y / n):");
        while (getchar() != '\n') {   //处理缓冲区\n (因为第一次scanf读取num1,operator,num2后,字符输入缓冲区中可能仍然包含前一次输入时按下的回车键 \n)
            ;
        }

        fgets(enter, sizeof(enter), stdin); // 使用 fgets 读取整行输入
        //下面检查输入的所有字符
        int i = 0, valid = 1;
        while (enter[i] != '\n') 
        {
            if ('y' != enter[i] && 'Y' != enter[i]) {
                valid = 0;//valid=0说明程序要退出
                break;
            }
            i++;
        }

        if (!valid) {
            break;//退出do-while大循环的条件
        }

        printf("\n");//调整格式,无实际含义
    } while (1);

    printf("总共执行的操作次数为:%d次\n", count);//打印操作次数
    printf("\n");
    
    return 0;
}

掷骰子

来源:61CDay05

编写程序模拟掷骰子的游戏(每一次投掷,都投掷两个骰子)。每局游戏的规则如下:

  1. 第一次掷的时候:
    1. 如果点数之和为 7 或 11 则获胜;
    2. 如果点数之和为2、3或12则落败;
    3. 其他情况下的点数之和称为“目标”,继续投掷两个骰子。
  2. 在后续的投掷中:
    1. 如果玩家再次掷出“目标”点数则获胜;
    2. 如果掷出7则落败;
    3. 其他情况都忽略,继续投掷两个骰子。

在每一局游戏结束时,程序都要询问用户是否再玩一次,如果用户输入的回答不是 y 或 Y ,那么就结束游戏,程序此时要打印显示胜败的次数。

玩家游玩过程程序的输出,大体如下所示:

You rolled: 5
Your point is 5
You rolled: 7
You lose!

Play again?(y/Y means continue) y
You rolled: 5
Your point is 5
You rolled: 4
You rolled: 4
You rolled: 9
You rolled: 2
You rolled: 7
You lose!

Play again?(y/Y means continue) y
You rolled: 4
Your point is 4
You rolled: 7
You lose!

Play again?(y/Y means continue) n

Wins: 0 Losses: 3

提示(请思考三个问题):

1.肯定需要使用随机数,那么srand设置种子值,这个函数调用应该放在哪里呢?

2.在键盘录入是否继续游玩时,如果键盘输入的是“(空格)y”,会发生什么事情?怎么解决?

3.在键盘录入是否继续游玩时,如果键盘输入的是“yyy”,会发生什么事情?怎么解决?

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <time.h>
#include <stdbool.h>

// 掷一次骰子,一次扔两个骰子,返回掷出的结果
int dice_sum(void)
{
	int point1, point2 ;

	point1 = rand() % 6 + 1;
	point2 = rand() % 6 + 1;

	printf("You rolled: %d\n", point1 + point2); // 打印两个骰子点数之和

	return point1 + point2;
}

// 玩一次游戏,返回true表示赢,false表示输了
bool play_game(void) {
	//第一次投骰子
	int point = dice_sum();
	if (7 == point || 11 == point) {
		printf("You win!\n");
		return true;
	}
	if (2 == point || 3 == point || 12 == point) {
		printf("You lose!\n");
		return false;
	}
	printf("Your point is %d\n", point); // 打印第一次掷出的结果

	// 第一次掷骰子不能决定输赢
	//后续投掷
	while (1)	//死循环,直到输或赢
	{
		int target = dice_sum();

		//输
		if (7 == target) {
			printf("You lose!\n");
			return false;
		}
		//赢
		if (point == target) {
			printf("You win!\n");
			return true;
		}
	}
}

int main(void) {
	srand(time(NULL));

	int win = 0, lose = 0;
	char again[100];

	do {
		play_game() ? win++ : lose++;

		printf("Play again?(y/Y means continue):");

		//处理输入 (处理输入有多个"yyyyy"    或者  输入"(空格)y" 也可以继续玩游戏的情况)
		fgets(again, sizeof(again), stdin);//将输入存入again字符串

		int i = 0, valid = 1;
		while (again[i] != '\n')
		{
			if (' ' == again[i]) {	//处理 "(空格)y" 这种输入情况,忽略输入的空格
				i++;
				continue;
			}
			if (again[i] != 'y' && again[i] != 'Y') {
				valid = 0;
			}
			i++;
		}

		//do-while大循环结束的条件
		if (!valid) {		//输入不是y 或者 Y,表示不玩了结束游戏
			break;
		}

		printf("\n");//调整格式
	} while (1);

	printf("\n**********Wins:%d,Losses:%d**********\n", win, lose);
	
	return 0;
}

注意点

  • fgets(again, sizeof(again), stdin);
    

将电脑屏幕的输入存入again字符串

  • if (3 != ret) {
               printf("Error: 输入的表达式格式不合法!请重新输入。\n\n");
               while (getchar() != '\n')		
                   ;
               continue;                       
           }
    

简易计算器中,if (3 != ret)校验输入参数,比如要求输入2个整数1个字符。(1 + 2),结果输成a + b,此时发生错误。
while (getchar() != '\n'用来清除缓冲区中错误的输入,避免影响以后的输入,比如清除a + b

  • valid = 1
    

控制do-while循环的退出

  • while (enter[i] != '\n') 
            {
                if ('y' != enter[i] && 'Y' != enter[i]) {
                    valid = 0;
                    break;
                }
                i++;
            }
    
            if (!valid) {
                break;
            }
    
  • while (again[i] != '\n')
    		{
    			if (' ' == again[i]) {	//处理 "(空格)y" 这种输入情况,忽略输入的空格
    				i++;
    				continue;
    			}
    			if (again[i] != 'y' && again[i] != 'Y') {
    				valid = 0;
    			}
    			i++;
    		}
    
    		if (!valid) {		
    			break;
    		}
    

参考答案

简易计算器(一)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void) {
    double num1, num2;
    char operator;
    double result;
    char flag = 'y';
    do
    {
        printf("请输入需要计算的表达式(例如:1 + 1): ");
        int ret = scanf("%lf %c %lf", &num1, &operator, &num2);
        if (ret != 3) {
            printf("Error: 输入的表达式格式不合法!请重新输入。\n\n");
            while (getchar() != '\n')
                ; // 清空缓冲区至行尾,避免影响下次输入
            continue; // 继续下一次循环
        }
        switch (operator) {
        case '+':
            result = num1 + num2;
            break;
        case '-':
            result = num1 - num2;
            break;
        case '*':
            result = num1 * num2;
            break;
        case '/': {
            if (num2 != 0) {
                result = num1 / num2;
            }
            else {
                printf("Error: 除数为0!请重新输入表达式。\n\n");
                continue;
            }
            break;
        }
        default:
            printf("Error: 无法识别的操作符!请重新输入表达式。\n\n");
            continue;
        }
        printf("计算的结果是:%.2lf %c %.2lf = %.2lf\n\n", num1, operator, num2, result);

        printf("是否继续计算?(输入'y/Y'继续,其他任意键结束): ");
        while (getchar() != '\n')
            ; // 清空缓冲区至行尾,避免影响下次输入
        scanf(" %c", &flag);
        printf("\n");
    } while (flag == 'y' || flag == 'Y');
    return 0;
}

简易计算器(二)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

// 全局变量,用于记录操作次数
int operation_count = 0;

// 函数声明
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
float divide(int a, int b);
void print_operation_count();

int main() {
 int a, b;
 char operator;
 char choice;

 do {
     printf("请输入要计算的表达式(例如,5 + 3): ");
     scanf(" %d %c %d", &a, &operator, &b);

     switch (operator) {
     case '+':
         printf("结果: %d\n", add(a, b));
         break;
     case '-':
         printf("结果: %d\n", subtract(a, b));
         break;
     case '*':
         printf("结果: %d\n", multiply(a, b));
         break;
     case '/':
         printf("结果: %.2f\n", divide(a, b));
         break;
     default:
         printf("无效的运算符。\n");
     }
     // 询问用户是否继续
     printf("是否继续? (y/n): ");
     scanf(" %c", &choice);
     printf("\n");
 } while (choice == 'y' || choice == 'Y');

 print_operation_count();
 return 0;
}

int add(int a, int b) {
 operation_count++;
 return a + b;
}

int subtract(int a, int b) {
 operation_count++;
 return a - b;
}

int multiply(int a, int b) {
 operation_count++;
 return a * b;
}

float divide(int a, int b) {
 if (b == 0) {
     printf("error:除数为零!\n");
     exit(1);
 }
 operation_count++;
 return (float)a / b;
}

void print_operation_count() {
 printf("总共执行的操作次数为: %d次\n", operation_count);
}

掷骰子

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>

// 玩一次游戏,返回true表示赢,false表示输了
bool play_game(void);
// 掷一次骰子,一次扔两个骰子,返回掷出的结果
int roll_dice(void);

int main(void) {
    int wins = 0, losses = 0;// 记录胜利和输的次数
    char again; // 决定用户是否继续玩
    // 设置随机种子,只需要设置一次,所以放在主函数里
    srand(time(NULL));
    do {
        play_game() ? wins++ : losses++;
        printf("\nPlay again?(y/Y means continue) ");
        // %c前面必须加空格,因为要跳过前面的空格输入
        scanf(" %c", &again);
        // 清空一次键盘录入后剩余的垃圾数据,避免对下一次输入产生影响
        while (getchar() != '\n')
            ;
    } while (again == 'Y' || again == 'y'); // 如果用户输入'Y'或'y',则继续玩

    // 游戏结束,打印赢和输的次数
    printf("\nWins: %d Losses: %d\n", wins, losses);

    return 0;
}

bool play_game(void) {
    int point = roll_dice();
    if (point == 7 || point == 11) {
        printf("You win!\n");
        return true;
    }
    if (point == 2 || point == 3 || point == 12) {
        printf("You lose!\n");
        return false;
    }
    // 第一次掷骰子不能决定输赢,于是继续掷骰子
    printf("Your point is %d\n", point); // 打印第一次掷出的结果,后续只要掷出它就赢了
    while (1) {     // 死循环,直到决定出输赢结束循环
        int tally = roll_dice(); // 再次掷骰子
        if (tally == point) { // 如果点数与之前相同,玩家赢
            printf("You win!\n");
            return true;
        }
        if (tally == 7) { // 如果掷出7点,玩家输
            printf("You lose!\n");
            return false;
        }
    }
}

int roll_dice() {
    int a = rand() % 6 + 1; 
    int b = rand() % 6 + 1; 
    printf("You rolled: %d\n", a + b); // 打印两个骰子点数之和

    return a + b; // 返回点数之和
}
  • 19
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值