[英]C - Trying to scanf hex/dec/oct values to check if they're equal to user input
因此,我是C的新手,這是我第一個上課的項目。 我基本上需要一個程序,詢問用戶他想要多少個問題,然后檢索最大的正8位數字(可以是oct / dec / hex(隨機)),然后要求用戶將其轉換為隨機數。 例如,如果我得到一個十進制數字,它將隨機要求我將其轉換為十六進制或八進制。 在每個問題的末尾,它都說明我的轉換是對還是錯,在程序的末尾,它表明我有多少對問題。
一切正常,直到當我要求我轉換為十六進制以外的其他字符時,我才開始輸入隨機字母/字符。 例如,如果它要求我將八進制轉換為十進制,如果我輸入一個字母,它有時會說“是對的”,並且它還會跳過問題並繼續循環直到得到十六進制。
我真的不知道該怎么辦。 這是我的代碼:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main()
{
int rightanswers = 0;
int answer;
int nquestions;
printf("Number of questions:");
scanf("%d", &nquestions);
srand((unsigned int) time(NULL));
unsigned char questions[nquestions];
for (int i=1; i<=nquestions; i++)
{
questions[i] = (rand()%255)+1;
int randomnumb = (rand()%6)+1;
switch(randomnumb)
{
case 1:
printf("\nConvert 0%o to base 10:", questions[i]);
scanf("%d", &answer);
if (answer == questions[i])
{
rightanswers++;
printf("Right!");
}
else
{
printf("Wrong!");
}
break;
case 2:
printf("\nConvert 0%o to base 16:", questions[i]);
scanf("%x", &answer);
if (answer == questions[i])
{
rightanswers++;
printf("Right!");
}
else
{
printf("Wrong!");
}
break;
case 3:
printf("\nConvert %d to base 8:", questions[i]);
scanf("%o", &answer);
if (answer == questions[i])
{
rightanswers++;
printf("Right!");
}
else
{
printf("Wrong!");
}
break;
case 4:
printf("\nConvert %d to base 16:", questions[i]);
scanf("%x", &answer);
if (answer == questions[i])
{
rightanswers++;
printf("Right!");
}
else
{
printf("Wrong!");
}
break;
case 5:
printf("\nConvert 0x%x to base 8:", questions[i]);
scanf("%o", &answer);
if (answer == questions[i])
{
rightanswers++;
printf("Right!");
}
else
{
printf("Wrong!");
}
break;
case 6:
printf("\nConvert 0x%x to base 10:", questions[i]);
scanf("%d", &answer);
if (answer == questions[i])
{
rightanswers++;
printf("Right!");
}
else
{
printf("Wrong!");
}
break;
}
}
printf("\nYou got %d conversions right!", rightanswers);
return 0;
}
繼續我的評論。 scanf
(及其家族)給新的C程序員帶來用戶輸入麻煩的地方,因為scanf
有很多陷阱。 這主要是因為轉換說明不同的表現有關領導whitepace的處理(如space
, tab
, newline
,等..)和程序員沒有驗證的回報 。 返回驗證至關重要,因為輸入緩沖區 (例如stdin
)中剩余的內容取決於是否成功進行轉換。
scanf
返回成功轉換的次數。 (例如scanf ("%s %d", strvar, &intvar)
)包含2
轉換說明符 ( %s
& %d
)。 如果成功將字符串和整數轉換並存儲在提供的變量中,則返回 2
。 較小的值表示匹配失敗或輸入失敗,或者用戶通過使用Ctrl + D (或在windoze上為Ctrl + Z )手動生成EOF
取消輸入。
如果由於匹配失敗 (輸入類型與轉換說明符之間的不匹配)或輸入失敗 (每個轉換說明符沒有足夠的輸入)而導致轉換失敗,則從stdin
讀取停止,不再從輸入中讀取其他字符緩沖區和所有的字符都剩下了-只是等待魚雷下一次調用scanf
。
此外,您必須考慮每次調用scanf
之后留在輸入緩沖區中的'\\n'
(由用戶按Enter生成)。 有些格式說明符將占用前導空格,而另一些格式說明 符 (如字符格式說明符則不會),實際上%c
會很樂意將stdin
左側的'\\n'
用作下一個輸入 。
具體來說,數字格式說明符(例如%d, %x, %o, %lf, ...
)將全部忽略前導空格,因此您不必在下一個scanf
調用之前專門刪除空格。 對於其他所有人,您都可以這樣做。 這種對stdin
剩余(或可能剩余)的解釋對於使用scanf
作為輸入至關重要。 否則,您只是要求輸入似乎被跳過或...無限循環。 (您可以使用制作格式字符串的方式來處理空格)
所有這些都是為什么建議使用諸如fgets
這樣的面向行輸入函數來處理用戶輸入的原因。 它將讀取直到並包括尾部的'\\n'
,這提供了一個簡單的檢查方法,以檢查是否正確讀取了用戶提供的所有輸入字符。 (在驗證用戶輸入后,您可以從fgets
(或POSIX getline
)填充的緩沖區中解析所需的內容
但是由於您會多次遇到scanf
,所以值得花時間閱讀(理解) man scanf
。 是的,這有點枯燥乏味,但這是唯一可以准確解釋scanf
陷阱所在的地方。
考慮到這一點,下面提供了兩個示例(對代碼的修改),以向您展示如何使用scanf
處理輸入。 對nquestions
的初讀僅顯示了一種通用方法,該方法可使用scanf
驗證整數輸入,檢查返回值,處理用戶取消操作(生成EOF
),最后清空通過簡單地從中讀取的幫助函數empty_stdin()
保留的所有字符。 stdin
直到'\\n'
或(通過用戶按壓輸入生成) EOF
被發現。
用戶輸入的其余部分由輔助函數getintvalue
處理(因為您不想在代碼中一遍又一遍地重復驗證代碼)。 getintvalue
將提示顯示並以格式字符串作為參數,但實際上所做的只是對nquestions
中的nquestions
做的事情。
其他變化。 您不需要questions[]
數組questions[]
。 一個簡單的int
值就可以了。 你並不需要在每個復制代碼case
的的switch
。 (已移至末尾)。 其余的更改和問題在下面的嵌入式注釋中得到了解決:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define PRMTSZ 128 /* if you need a constant define one */
/* or use an enum to define several */
enum { NSWITCH = 6, QMAX = 256 };
/* function to get integer value from user.
* prompt user with 'prompt', read value with format 'fmt'.
* returns int value or EOF on user cancelation of input.
*/
int getintvalue (const char *prompt, const char *fmt);
/* simple function to empty remaining chars from stdin */
void empty_stdin();
int main (void) {
int rightanswers = 0,
nquestions = 0;
srand (time(NULL));
/* example input loop -- loop continually until valid input or EOF */
for (;;) {
int rtn = 0;
printf ("Number of questions: ");
if ((rtn = scanf ("%d", &nquestions)) == EOF) {
fprintf (stderr, "warning: user canceled input.\n");
return 1;
}
empty_stdin(); /* remove all remaining chars from stdin */
if (rtn == 1) /* good input */
break;
/* handle matching or input failure */
fprintf (stderr, "error: invalid input.\n");
}
/* loops are ZERO based in C */
for (int i = 0; i < nquestions; i++)
{
/* declarations 1st in each block, for C89 portability - Win7, etc. */
char prompt[PRMTSZ] = ""; /* buffer for prompt */
int randomnumb = rand() % NSWITCH, /* values 0 - 5 */
question = rand() % QMAX, /* values 0 - 255 */
answer = 0;
switch (randomnumb)
{
case 0:
sprintf (prompt, "\nConvert 0%o to base 10: ", question);
/* let's use a getintvalue to validate user int input */
if ((answer = getintvalue (prompt, "%d")) == EOF)
return 1;
break;
case 1:
sprintf (prompt, "\nConvert 0%o to base 16: ", question);
if ((answer = getintvalue (prompt, "%x")) == EOF)
return 1;
break;
case 2:
sprintf (prompt, "\nConvert %d to base 8: ", question);
if ((answer = getintvalue (prompt, "%o")) == EOF)
return 1;
break;
case 3:
sprintf (prompt, "\nConvert %d to base 16: ", question);
if ((answer = getintvalue (prompt, "%x")) == EOF)
return 1;
break;
case 4:
sprintf (prompt, "\nConvert 0x%x to base 8: ", question);
if ((answer = getintvalue (prompt, "%o")) == EOF)
return 1;
break;
case 5:
sprintf (prompt, "\nConvert 0x%x to base 10: ", question);
if ((answer = getintvalue (prompt, "%d")) == EOF)
return 1;
break;
default:
fprintf (stderr, "error: something went wrong in switch.\n");
goto badswitch;
break;
}
if (answer == question) {
rightanswers++;
printf ("Right!\n");
}
else
printf ("Wrong!\n");
badswitch:;
}
/* always end with '\n' for POSIX compiant EOF */
printf("\nYou got %d conversions right!\n", rightanswers);
return 0;
}
int getintvalue (const char *prompt, const char *fmt)
{
int value = 0;
/* input loop -- loop continually until valid input or EOF */
for (;;) {
int rtn = 0;
printf ("%s: ", prompt);
if ((rtn = scanf (fmt, &value)) == EOF) {
fprintf (stderr, "warning: user canceled input.\n");
return rtn;
}
empty_stdin(); /* remove all remaining chars from stdin */
if (rtn == 1) /* good input */
break;
/* handle matching or input failure */
fprintf (stderr, "error: invalid input.\n");
}
return value;
}
void empty_stdin()
{
int c;
do
c = getchar();
while (c != '\n' && c != EOF);
}
以下是各種驗證運行,它們顯示了程序各個階段對錯誤輸入或用戶取消的正確處理。
一切正常的示例
$ ./bin/inttestscanf
Number of questions: 4
Convert 218 to base 16: : da
Right!
Convert 0325 to base 10: : 213
Right!
Convert 0xe to base 10: : 14
Right!
Convert 0x39 to base 8: : 71
Right!
You got 4 conversions right!
用戶取消問題數時的示例
$ ./bin/inttestscanf
Number of questions: foo
error: invalid input.
Number of questions: bar
error: invalid input.
Number of questions: warning: user canceled input.
用戶輸入無效輸入時的示例
$ ./bin/inttestscanf
Number of questions: 4
Convert 023 to base 16: : no good
error: invalid input.
Convert 023 to base 16: : 13
Right!
Convert 0xc7 to base 8: : foo
error: invalid input.
Convert 0xc7 to base 8: : 307
Right!
Convert 0353 to base 16: : eb
Right!
Convert 0x76 to base 10: : f8
error: invalid input.
Convert 0x76 to base 10: : 118
Right!
You got 4 conversions right!
( 注意:考慮一下scanf
期望一個十六進制值並且用戶輸入以af
開頭的任意字符串會發生什么情況?)
仔細檢查一下,如果您還有其他問題,請告訴我。
如果scanf
遇到無法分配的內容,它將停止並返回。 您嘗試分配的變量將不會更改-但您會像讀取它一樣檢查它的值。 它只會包含之前的內容或隨機的內容。
要使用scanf,您需要始終檢查其返回值 ,它告訴您分配了多少個變量。 僅當此計數顯示您的變量已被實際分配時,您才可以訪問其內容。
請注意,失敗的scanf之后,“不可讀”數據仍將存在於輸入流中,下一個scanf將嘗試再次讀取相同的數據。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.