2010-08-06 21 views
56

gcc 4.4.4 c89sscanf或atoi將字符串轉換爲整數有什麼區別?

將字符串轉換爲整數值更好。

我已經嘗試了2種不同的方法atoi和sscanf。兩者都按預期工作。

char digits[3] = "34"; 
int device_num = 0; 

if(sscanf(digits, "%d", &device_num) == EOF) { 
    fprintf(stderr, "WARNING: Incorrect value for device\n"); 
    return FALSE; 
} 

或使用的atoi

device_num = atoi(digits); 

我在想,在sscanf的效果會更好,你可以檢查錯誤。但是,atoi沒有做任何檢查。

+0

可能出現[將字符串轉換爲整數C]的副本(http://stackoverflow.com/questions/7021725/converting-string-to-integer- c) – 2016-05-11 18:07:13

回答

98

使用ISDIGIT你有3種選擇:

  1. atoi

這可能是最快的,如果你在性能關鍵的代碼中使用它,但它確實沒有錯誤報告。如果字符串不是以整數開頭,則返回0.如果字符串在整數後面包含垃圾,它將轉換初始部分並忽略其餘部分。如果該數字太大而不適合int,則行爲未指定。

  • sscanf
  • 一些錯誤報告,你有什麼類型的存儲(的char/short/int/long/long long/size_t/ptrdiff_t/intmax_t符號/無符號的版本),具有很大的靈活性。

    返回值是成功轉換的數量,因此如果字符串不是以整數開頭,則"%d"的掃描將返回0。您可以使用"%d%n"來存儲在另一個變量中讀取的整數之後的第一個字符的索引,從而檢查整個字符串是否已轉換或之後是否有垃圾。但是,與atoi一樣,未指定整數溢出的行爲。

  • strtol和家庭
  • 魯棒錯誤報告,提供設置errno爲0進行調用之前。返回值在溢出時指定,並且將設置爲errno。您可以選擇2到36之間的任意數字基數,或者指定0作爲基數,以自動解釋前導0x0分別爲十六進制和八進制。要轉換爲的類型的選項是long/long long/intmax_t的有符號/無符號版本。

    如果您需要較小的類型,您可以隨時將結果存儲在臨時變量longunsigned long中,並檢查是否溢出。

    因爲這些函數接受指針參數的指針,所以您還可以獲得一個指向轉換整數後的第一個字符的指針(免費),以便您可以判斷整個字符串是整數還是解析字符串中的後續數據if需要。


    就個人而言,我會建議strtol家族目的。如果你正在做一些快速而又骯髒的事情,atoi可能會滿足你的需求。

    順便說一下,有時我發現我需要解析數字,其中領先的空格,符號等不應被接受。在這種情況下,它是很該死容易推出自己的for循環,例如,

    for (x=0; (unsigned)*s-'0'<10; s++) 
        x=10*x+(*s-'0'); 
    

    或者你可以使用(健壯性):

    if (isdigit(*s)) 
        x=strtol(s, &s, 10); 
    else /* error */ 
    
    +0

    'errno'在'strtol'中是strtol(3)手冊頁中規定的實現特定功能。要正確驗證,您應該通過endptr。如果** endptr在'strtol'後面是'\ 0',那麼該字符串將被作爲一個整體進行解析並且是有效的(或者其長度爲零)。 – Zouppen 2014-03-05 21:20:29

    +1

    @Zouppen:不知道你從哪裏得到這些信息,但這是錯誤的。 「strtol,strtoll,strtoul和strtoull函數會返回轉換的值(如果有的話)。如果不能執行轉換,則返回零。如果正確值超出了可表示值的範圍,則返回LONG_MIN,LONG_MAX,LLONG_MIN,LLONG_MAX,ULONG_MAX或ULLONG_MAX(根據返回類型和值的符號(如果有)),並且宏ERANGE的值爲存儲在errno中「(C99 7.20.1.4段落8) – 2014-03-05 23:39:11

    +0

    然而,你是對的,你需要檢查其他條件,只有溢出是一個」錯誤「,沒有進行任何轉換應該通過'endptr'來檢測,如果你堅持要消耗整個字符串,你也應該檢查一下。 – 2014-03-05 23:40:55

    9

    *scanf()函數族返回已轉換值的數量。所以你應該檢查以確保sscanf()在你的情況下返回1。 EOF返回「輸入失敗」,這意味着ssacnf()將永不返回EOF

    對於sscanf(),函數必須解析格式字符串,然後解碼整數。 atoi()沒有那個開銷。兩者都遭遇到超出範圍值導致未定義行爲的問題。

    您應該使用strtol()strtoul()函數,它們提供更好的錯誤檢測和檢查。他們還會告訴你,如果整個字符串被消耗。

    如果你想要一個int,你可以隨時使用strtol(),然後檢查返回值,看它是否位於INT_MININT_MAX之間。

    +0

    作爲'strtol'等的附加獎勵,如果您將* base *設置爲'0',則可以自動選擇八進制,十進制或十六進制轉換。 – 2010-08-06 06:09:47

    +0

    使用基數0的一個潛在問題是,以'0'開始的字符串將被解釋爲基數8(八進制)。這種行爲是有知識的用戶期望的,但太多的人不_octal_意識到,並且驚訝地發現'012'變成10而'019'變成1,因爲由於非八進制數字9而停止轉換。 – chux 2013-06-11 04:02:34

    0

    如果用戶輸入34abc和你通過他們ATOI它會返回34 如果你想驗證,然後輸入值,你必須對輸入的字符串反覆

    4

    要@R ..我認爲這是不夠的在strtol調用中檢查errno的錯誤檢測。

    long strtol (const char *String, char **EndPointer, int Base) 
    

    您還需要檢查EndPointer是否有錯誤。

    2

    結合R.,和PickBoy答案爲簡潔

    long strtol (const char *String, char **EndPointer, int Base) 
    
    // examples 
    strtol(s, NULL, 10); 
    strtol(s, &s, 10); 
    
    2

    當沒有關於無效的字符串輸入或範圍問題的關注,用最簡單的:atoi()

    否則,該方法以最佳的錯誤/範圍檢測既不是atoi()也不是sscanf()This good answer所有準備好的詳細信息缺少錯誤檢查atoi()一些錯誤檢查sscanf()

    strtol()是將字符串轉換爲int時最嚴格的函數。但這只是一個開始。下面是詳細的例子,以顯示正確的用法,以及在accepted one之後回答這個問題的原因。

    // Over-simplified use 
    int strtoi(const char *nptr) { 
        int i = (int) strtol(nptr, (char **)NULL, 10); 
        return i; 
    } 
    

    這是等atoi(),而忽略使用的strtol()錯誤檢測功能。

    要充分利用strtol(),有各種功能來考慮:

    1. 檢測沒有轉換例子:"xyz",或"""--0"?在這些情況下,endptr將匹配nptr

      char *endptr; 
      int i = (int)strtol(nptr, &endptr, 10); 
      if (nptr == endptr) return FAIL_NO_CONVERT; 
      
    2. 如果整個字符串轉換或只是開頭部分:是"123xyz" OK?

      char *endptr; 
      int i = (int)strtol(nptr, &endptr, 10); 
      if (*endptr != '\0') return FAIL_EXTRA_JUNK; 
      
    3. 檢測是否值這麼大,結果不表示爲一個long"999999999999999999999999999999"

      errno = 0; 
      long L = strtol(nptr, &endptr, 10); 
      if (errno == ERANGE) return FAIL_OVERFLOW; 
      
    4. 檢測如果該值是比int的範圍之外,但不long。如果intlong具有相同的範圍,則不需要此測試。

      long L = strtol(nptr, &endptr, 10); 
      if (L < INT_MIN || L > INT_MAX) return FAIL_INT_OVERFLOW; 
      
    5. 一些實現超越C標準,並設置errno額外的原因,如errno to EINVAL in case no conversion was performedEINVAL The value of the Base parameter is not valid.。測試這些errno值的最佳時間取決於實現。

    把所有這些組合起來:(按需要調整)

    #include <errno.h> 
    #include <stdlib.h> 
    
    int strtoi(const char *nptr, int *error_code) { 
        char *endptr; 
        errno = 0; 
        long i = strtol(nptr, &endptr, 10); 
    
        #if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX 
        if (errno == ERANGE || i > INT_MAX || i < INT_MIN) { 
        errno = ERANGE; 
        i = i > 0 : INT_MAX : INT_MIN; 
        *error_code = FAIL_INT_OVERFLOW; 
        } 
        #else 
        if (errno == ERANGE) { 
        *error_code = FAIL_OVERFLOW; 
        } 
        #endif 
    
        else if (endptr == nptr) { 
        *error_code = FAIL_NO_CONVERT; 
        } else if (*endptr != '\0') { 
        *error_code = FAIL_EXTRA_JUNK; 
        } else if (errno) { 
        *error_code = FAIL_IMPLEMENTATION_REASON; 
        } 
        return (int) i; 
    } 
    

    注:提到的所有功能,允許前導空格,可選的領先標誌性格和區域受影響更改。更多限制性轉換需要附加代碼。


    注:非OP標題更改傾斜重點。此答案更適用於原始標題「將字符串轉換爲整數sscanf或atoi」