簡體   English   中英

C-嘗試掃描十六進制/十進制/八進制值以檢查它們是否等於用戶輸入

[英]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的處理(如spacetabnewline ,等..)和程序員沒有驗證的回報 返回驗證至關重要,因為輸入緩沖區 (例如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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM