關於獲取0~9共10個數字的測試程序——防禦性編程超進化

前言

“防禦性編程超進化”,標題似乎有點中二,但是這更能體現我苦思冥想終於解決這一問題時激動的心情,索性就不改了。
注:我終於還是把名字改了,閱讀數量實在是太低了,標題也沒有說出這篇博文的關鍵信息。


目的和目標

相信大家有有過這種體驗,我希望用戶輸入數字,用戶偏要輸入一個英文字符或其他標點符號,成功導致程序崩潰。這個用戶也許是自己,也許是其他測試人員,也許是老師等等。
今天我給大家介紹一下我關於防禦性編程的一點體會和心得。下面的代碼是一個遞進的過程,不斷更新後一定程度上已經可以正確獲取用戶輸入的0到9以內的數字,而不會使程序崩潰,提高了程序的健壯性。


近期在學軟件構造,學到了防禦性編程的概念,應用到了數據結構課程設計中,在此記錄。

C語言程序中常常使用用戶輸入的整數確定程序調用的功能模塊,但用戶輸入的選項多種多樣,你讓用戶輸入一個整數,用戶也許會輸入一串字符,這常常會引起程序的崩潰,現在分享一下我的解決思路,“請慢用”。


程序

測試數據有:{1, 2, 3, 4, 。, 3a, a3, 0} (共八個測試數據)。


第一次嘗試

  1. 剛開始時,我們通過scanf("%d", &option);獲取用戶的選項。
#include <stdio.h>

int main (void)
{
	int option;			// 選項 
	int endFlag = 0;	// 結束標誌,1退出程序,0繼續程序 
	while(1) {
		scanf("%d", &option);
		switch (option) {
			case 1: printf("功能1\n");break;
			case 2: printf("功能2\n");break;
			case 3: printf("功能3\n");break;
			case 0: printf("退出\n");endFlag=1;break;
			default :break;
		}
		if (endFlag != 0) {
			break;
		}
	}
	return 0;
}

測試結果:
測試結果1
可以看出,輸入“。”後程序進入無限循環,重複輸出“功能3”,程序出錯。


第二次嘗試

  1. 之後的簡單演變不說了,跳到近期實現的獲取選項的代碼,單獨定義一個函數用於輸入選項。
#include <stdio.h>
int selectOption(int smaller, int bigger); 
int main (void)
{
	int option;			// 選項 
	int endFlag = 0;	// 結束標誌,1退出程序,0繼續程序 
	while(1) {
		option = selectOption(0,3); // 取值0,1,2,3 
		switch (option) {
			case 1: printf("功能1\n");break;
			case 2: printf("功能2\n");break;
			case 3: printf("功能3\n");break;
			case 0: printf("退出\n");endFlag=1;break;
			default :break;
		}
		if (endFlag != 0) {
			break;
		}
	}
	return 0;
}

int selectOption(int smaller, int bigger)	// 兩個參數表示範圍,要求:smaller < 返回值 < bigger
{
	int option = 0;
	char cOption; 
	printf ("請輸入選項(%d~%d):", smaller, bigger);
	scanf ("%c", &cOption);						// 輸入選擇的操作,字符錄入,防禦性編程 
	getchar();                                  // 提取回車符
	option = cOption-'0';                       // 得出數字 
	while (option<smaller || option>bigger)		// 當輸入的選擇不在smaller到bigger時,重新選擇 
	{
		printf ("該選項不存在,請重新輸入:");
		scanf ("%d", &option);					//輸入選項 
		getchar();
	}
	
	return option;
}

測試結果:
測試結果2
測試分析:

測試用例 預期結果 是否符合預期
1 功能1
2 功能2
3 功能3
4 重新輸入
重新輸入 否,在下一次輸入時把上次的回車符錄入了
3a 功能3
a3 無效輸入 否,把a和3分別錄入了
0 退出

—輸入“a3”,程序判斷選項“a”不存在,並且將“3”當成了下一次循環輸入的字符,所以在原先輸入選項的位置輸出了“功能3”;


第三次嘗試

  1. 超進化
// 這是防禦性編程超進化 
int selectOption(int smaller, int bigger)	// 兩個參數表示範圍,要求: smaller < 返回值 < bigger
{
	int maxsize = 100; 
	int option = 0;	// 選項 
	int i;
	int length;     // 字符串長度 
	char sOption[maxsize];  // 存儲字符串的數組 
	int trueOptionFlag = 0; 
	printf ("請輸入選項(%d~%d):", smaller, bigger);
	
	while (1) {
		gets(sOption);	// gets() 不獲取回車符,需不需要getchar()一下呢?經驗證不需要,似乎已經處理掉了回車符 
		// gets()函數用來從標準輸入設備(鍵盤)讀取字符串直到回車結果,但回車不屬於這個字符串,系統自動用'\0'代替最後的換行符。
		length = strlen(sOption);   // #include <string.h>
	    if (sOption[0] == '\000') { // 即length=0,也就是隻輸入了回車符,沒有輸入其他字符的情況 
	    	continue;
		}
		// 遍歷一遍字符串,過濾掉無效字符,直到查找到符合條件的字符才停止遍歷,並標記 
		for (i=0; i<length; i++) {
			if (sOption[i] >= '0'+smaller && sOption[i] <= '0'+bigger) {
				// 已過濾掉無效字符,你輸入的選項是:... 
				option = sOption[i]-'0'; 
				trueOptionFlag = 1;
				break;
			}
		}
		if (trueOptionFlag != 1) {
			printf ("該選項不存在,請重新輸入(%d~%d):", smaller, bigger);
		} else {
			int j;
			if (i > 0) {  // 第一個字符無效時輸出 
				printf("已過濾無效字符:");
				for (j=0; j<i; j++) {
					putchar(sOption[j]);
				} 
				putchar('\n');
			}
			printf("你輸入的選項是%d\n", option);
			break;
		}
	}
	return option;
}

測試結果:
測試結果3
測試分析:

測試用例 預期結果 是否符合預期
1 功能1
2 功能2
3 功能3
4 選項不存在,重新輸入
選項不存在,重新輸入
3a 功能3
a3 過濾掉無效字符,功能3
0 退出

後記

以上就是我根據對防禦性編程的理解進行的應用實踐了,到這裏就和大家說再見了,如果感覺收穫滿滿,記得贊一個啦;如果有什麼值得改進的地方,歡迎大家留言,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章