簡體   English   中英

將十六進制輸入存儲到 int 變量中而不使用 scanf() function in C

[英]Store hex input into int variable without using scanf() function in C

Pre-History:我遇到了一個問題,即 getchar() function 沒有以正確的方式得到處理,因為沒有對任何給定輸入的請求,程序只是繼續進一步處理。

I searched the internet about what this issue could be and found the information that if the scanf() function is implemented into a program before the getchar() function, the getchar() function does not behave in the right way, and would act like我的問題是。

引文:

我敢打賭,你只有在調用 getchar() 之前有一個 scanf() 時才會看到這個問題。

不要將 scanf 用於交互式程序。 這有兩個主要原因:

1) scanf 無法從格式錯誤的輸入中恢復。 您必須每次都正確獲取格式字符串,否則它只會丟棄無法匹配的任何輸入並返回指示失敗的值。 如果您在解析固定格式文件時無論如何都無法恢復不良格式,這可能會很好,但這與您想要對用戶輸入執行的操作完全相反。 使用 fgets() 和 sscanf()、fgets() 和 strtok(),或者使用 getchar() 和 putchar() 編寫您自己的用戶輸入例程。

1.5) 即使使用得當,scanf 也不可避免地會丟棄有時可能很重要的輸入(空白)。

2) scanf 有一個討厭的習慣,就是在輸入 stream 中留下換行符。 如果您只使用 scanf,這很好,因為 scanf 通常會跳過任何空白字符,因為它急於找到接下來要查找的任何內容。 但是,如果您將 scanf 與 fgets/getchar 混合使用,那么試圖弄清楚輸入 stream 中可能或可能不會留下的內容很快就會變得一團糟。 特別是如果您進行任何循環 - 輸入 stream 在第一次迭代中不同是很常見的,這會導致潛在的奇怪錯誤甚至更奇怪的修復嘗試。

tl; dr -- scanf 用於格式化輸入。 用戶輸入未格式化。 //

這是該線程的鏈接: https://bbs.archlinux.org/viewtopic.php?id=161294


scanf() 與:

scanf("%x",integer_variable); 

在我看來,作為現場的新手,這是從鍵盤輸入十六進制數字(或者更好地說是標准輸入文件)並將其存儲到 int 變量的唯一方法。

是否有其他方法可以從標准輸入輸入十六進制值並將其存儲到 integer 變量中?

額外挑戰:如果我可以將負值(當然通過負十六進制輸入)寫入有符號的 int 變量,那也很好。


信息:我已經在 Stackoverflow 上閱讀了許多關於 C 的關於類似問題的主題,但這些主題都沒有很好地回答我的明確問題。 所以我發布了這個問題。

我在 Linux Ubuntu 下工作。

關於百元賭注的報價是准確的。 scanfgetchar混合幾乎總是一個壞主意。 它幾乎總是會導致麻煩。 但這並不是說它們不能一起使用。 可以一起使用它們——但通常,這太難了。 有太多繁瑣的小細節和“陷阱”無法追蹤。 這比它的價值更麻煩。

一開始你說過

scanf() with... %d ... 對我來說,這似乎是從鍵盤輸入十六進制數字的唯一方法

那里有些混亂,因為當然%d用於十進制輸入。 但是,由於在您糾正該問題時我已經寫了這個答案,所以我們現在繼續使用小數。 (目前我也忽略了錯誤檢查——也就是說,如果用戶沒有輸入請求的數字,這些代碼片段不會檢查或做任何優雅的事情。)無論如何,這里有幾種讀取integer:

  1. scanf("%d", &integer_variable);
    你是對的,這是(表面上)最簡單的方法。

  2. char buf[100];
    fgets(buf, sizeof(buf), stdin);
    integer_variable = atoi(buf);
    我認為這是不使用scanf的最簡單方法。 但是現在大多數人都不喜歡使用atoi ,因為它沒有做太多有用的錯誤檢查。

  3. char buf[100];
    fgets(buf, sizeof(buf), stdin);
    integer_variable = strtol(buf, NULL, 10);
    這與以前幾乎相同,但避免使用atoi以支持首選的strtol

  4. char buf[100];
    fgets(buf, sizeof(buf), stdin);
    sscanf(buf, "%d", &integer_variable);
    這會讀取一行,然后使用sscanf對其進行解析,這是另一種流行且通用的技術。

所有這些都將起作用; 所有這些都將處理負數。 不過,考慮錯誤條件很重要——我稍后會對此進行更多說明。

如果要輸入十六進制數,技術類似:

  1. scanf("%x", &integer_variable);

  2. char buf[100];
    fgets(buf, sizeof(buf), stdin);
    integer_variable = strtol(buf, NULL, 16);

  3. char buf[100];
    fgets(buf, sizeof(buf), stdin);
    sscanf(buf, "%x", &integer_variable);

這些也都應該起作用。 不過,我不一定期望他們處理“負十六進制”,因為這是一個不尋常的要求。 大多數時候,十六進制表示法用於無符號整數。 (實際上,嚴格來說,帶有scanfsscanf%x必須與已聲明為unsigned int而非普通intinteger_variable一起使用。)

有時“手工”做這類事情是有用或必要的。 這是一個讀取兩個十六進制數字的代碼片段。 我將從使用getchar的版本開始:

int c1 = getchar();
if(c1 != EOF && isascii(c1) && isxdigit(c1)) {
    int c2 = getchar();
    if(c2 != EOF && isascii(c2) && isxdigit(c2)) {
        if(isdigit(c1)) integer_variable = c1 - '0';
        else if(isupper(c1)) integer_variable = 10 + c1 - 'A';
        else if(islower(c1)) integer_variable = 10 + c1 - 'a';

        integer_variable = integer_variable * 16;

        if(isdigit(c2)) integer_variable += c2 - '0';
        else if(isupper(c2)) integer_variable += 10 + c2 - 'A';
        else if(islower(c2)) integer_variable += 10 + c1 - 'a';
    }
}

如您所見,這有點令人震驚。 我,雖然我幾乎從不使用scanf家族的成員,但這是我有時會做的一個地方,正是因為“手工”做的工作量很大。 您可以通過使用輔助 function 或宏進行數字轉換來大大簡化它:

int c1 = getchar();
if(c1 != EOF && isascii(c1) && isxdigit(c1)) {
    int c2 = getchar();
    if(c2 != EOF && isascii(c2) && isxdigit(c2)) {
        integer_variable = Xctod(c1);
        integer_variable = integer_variable * 16;
        integer_variable += Xctod(c2);
    }
}

或者你可以把那些內心的表達折疊成

        integer_variable = 16 * Xctod(c1) + Xctod(c2);

這些工作在輔助 function 方面:

int Xctod(int c)
{
    if(!isascii(c)) return 0;
    else if(isdigit(c)) return c - '0';
    else if(isupper(c)) return 10 + c - 'A';
    else if(islower(c)) return 10 + c - 'a';
    else return 0;
}

或者也許是一個宏(盡管這絕對是一種老派的東西):

#define Xctod(c) (isdigit(c) ? (c) - '0' : (c) - (isupper(c) ? 'A' : 'a') + 10)

通常我不是使用stdin getchar()從標准輸入解析這樣的十六進制數字,而是從字符串中解析。 我經常使用字符指針 ( char *p ) 來遍歷字符串,這意味着我最終得到的代碼更像這樣:

char c1 = *p++;
if(isascii(c1) && isxdigit(c1)) {
    char c2 = *p++;
    if(isascii(c2) && isxdigit(c2))
        integer_variable = 16 * Xctod(c1) + Xctod(c2);
}

忽略臨時變量和錯誤檢查並將其進一步簡化是很誘人的:

integer_variable = 16 * Xctod(*p++) + Xctod(*p++);

但是不要這樣做,除了缺少錯誤檢查之外,這個表達式可能是undefined ,它肯定不會總是做你想要的,因為不再保證你讀取字符的順序。如果你知道p指向兩個十六進制數字中的第一個,您不想將其折疊得更遠

integer_variable = Xctod(*p++);
integer_variable = 16 * integer_variable + Xctod(*p++);

即便如此,這只適用於 Xctod 的Xctod版本,而不是宏,因為宏會多次評估其參數。


最后,讓我們談談錯誤處理。 有很多可能性需要擔心:

  1. 用戶在沒有輸入任何內容的情況下點擊 Return。
  2. 用戶在數字之前或之后鍵入空格。
  3. 用戶在數字后鍵入額外的垃圾。
  4. 用戶鍵入非數字輸入而不是數字。
  5. 代碼到達文件結尾; 根本沒有字符可供閱讀。

然后如何處理這些取決於您使用的輸入技術。 以下是基本規則:

A. 如果您正在調用scanffscanfsscanf ,請始終檢查返回值。 如果它不是 1(或者,在您有多個%說明符的情況下,這不是您希望讀取的值的數量),則意味着出現問題。 這通常會捕獲問題 4 和 5,並且會優雅地處理案例 2。 但它通常會悄悄地忽略問題 1 和 3。(特別是, scanffscanf將額外的\n視為前導空格。)

B. 如果您再次調用fgets ,請始終檢查返回值。 您將在 EOF 上獲得NULL (問題 5)。 處理其他問題取決於您對閱讀的行的處理方式。

C。 如果你調用atoi ,它會優雅地處理問題 2,但它會忽略問題 3,它會悄悄地將問題 4 變成數字 0(這就是通常不再推薦使用atoi的原因)。

D. 如果您調用strtol或任何其他“strto”函數,它們將優雅地處理問題 2,如果您讓它們返回“結束指針”,您可以檢查並處理問題 3 和4.(請注意,我在上面的兩個strtol示例中省略了結束指針處理。)

E. 最后,如果你正在做一些像我的“硬件”兩位數十六進制轉換器這樣的低級和骯臟的事情,你通常必須明確地自己解決所有這些問題。 如果你想跳過前導空格,你必須這樣做(來自<ctype.h>isspace function 可以提供幫助),如果可能有意外的非數字字符,你也必須檢查這些字符。 (這就是對isasciiisxdigit的調用在我的“硬件”兩位數十六進制轉換器中所做的事情。)

根據scanf手冊頁,您可以使用 scanf 將十六進制數從標准輸入讀取到(無符號)integer 變量中。

unsigned int v ;
if ( scanf("%x", &v) == 1 ) {
   // do something with v.
}

根據手冊頁, %x始終是無符號的。 如果要支持負值,則必須添加顯式邏輯。

如您發布的鏈接中所述,使用fgetssscanf是處理此問題的最佳方法。 fgets將讀取整行文本,而sscanf將解析該行。

例如

char line[100];
fgets(line, sizeof(line), stdin);

int x;
int rval = sscanf(line, "%x", &x);
if (rval == 1) {
    printf("read value %x\n", x);
} else {
    printf("please enter a hexadecimal integer\n");
}

由於您只閱讀單個 integer,因此您也可以使用strtol而不是sscanf 這還具有檢測是否輸入了任何其他字符的優點:

char *ptr;
errno = 0;
long x = strtol(line, &ptr, 16);
if (errno) {
    perror("parsing failed");
} else if (*ptr != '\n' && *ptr != 0) {
    printf("extra characters entered: %s\n", ptr);
} else {
    printf("read value %lx\n", x);
}    

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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