簡體   English   中英

字符串(const char *,size_t)轉換為int嗎?

[英]String (const char*, size_t) to int?

將(const char *,size_t)表示的字符串轉換為int的最快方法是什么?

該字符串不是以空值結尾的。 這兩種方式都涉及一個字符串復制(以及更多),我想避免這樣做。

是的,此功能每秒被調用幾百萬次。 :p

int to_int0(const char* c, size_t sz)
{
    return atoi(std::string(c, sz).c_str());
}

int to_int1(const char* c, size_t sz)
{
    return boost::lexical_cast<int>(std::string(c, sz));
}

給定一個這樣的字符串,您可以自己進行轉換以加快速度。 但是,根據代碼需要具有的魯棒性,這可能相當困難。 目前,讓我們假設最簡單的情況-我們確定字符串是有效的,僅包含數字(目前沒有負數),並且它表示的數字始終在int范圍內。 對於這種情況:

int to_int2(char const *c, size_t sz) { 
    int retval = 0;
    for (size_t i=0; i<sz; i++)
        retval *= 10;
        retval += c[i] -'0';
    }
    return retval;
}

從那里,您可以隨心所欲地進行復雜處理-處理前導/尾隨空格,'-'(但是對於2的補碼中的最大負數正確地執行操作並不總是那么簡單[編輯:請參閱Nawaz的答案作為一種解決方案到此]),數字分組等。

對於uint32的另一個版本:

void str2uint_aux(unsigned& number, unsigned& overflowCtrl, const char*& ch)
{
    unsigned digit = *ch - '0';
    ++ch;

    number = number * 10 + digit;

    unsigned overflow = (digit + (256 - 10)) >> 8;
    // if digit < 10 then overflow == 0
    overflowCtrl += overflow;
}

unsigned str2uint(const char* s, size_t n)
{
    unsigned number = 0;
    unsigned overflowCtrl = 0;

    // for VC++10 the Duff's device is faster than loop
    switch (n)
    {
    default:
        throw std::invalid_argument(__FUNCTION__ " : `n' too big");

    case 10: str2uint_aux(number, overflowCtrl, s);
    case  9: str2uint_aux(number, overflowCtrl, s);
    case  8: str2uint_aux(number, overflowCtrl, s);
    case  7: str2uint_aux(number, overflowCtrl, s);
    case  6: str2uint_aux(number, overflowCtrl, s);
    case  5: str2uint_aux(number, overflowCtrl, s);
    case  4: str2uint_aux(number, overflowCtrl, s);
    case  3: str2uint_aux(number, overflowCtrl, s);
    case  2: str2uint_aux(number, overflowCtrl, s);
    case  1: str2uint_aux(number, overflowCtrl, s);
    }

    // here we can check that all chars were digits
    if (overflowCtrl != 0)
        throw std::invalid_argument(__FUNCTION__ " : `s' is not a number");

    return number;
}

為什么這么慢? 因為它一一處理字符。 如果我們保證可以訪問直到s+16字節,則可以對*ch - '0'digit + 246使用向量化。
像下面的代碼:

    uint32_t digitsPack = *(uint32_t*)s - '0000';
    overflowCtrl |= digitsPack | (digitsPack + 0x06060606); // if one byte is not in range [0;10), high nibble will be non-zero
    number = number * 10 + (digitsPack >> 24) & 0xFF;
    number = number * 10 + (digitsPack >> 16) & 0xFF;
    number = number * 10 + (digitsPack >> 8) & 0xFF;
    number = number * 10 + digitsPack & 0xFF;
    s += 4;

范圍檢查的小更新:
第一個代碼段在每次迭代中都有多余的shift(或mov ),因此應該

unsigned digit = *s - '0';
overflowCtrl |= (digit + 256 - 10);
...
if (overflowCtrl >> 8 != 0) throw ...

最快的:

int to_int(char const *s, size_t count)
{
     int result = 0;
     size_t i = 0 ;
     if ( s[0] == '+' || s[0] == '-' ) 
          ++i;
     while(i < count)
     {
          if ( s[i] >= '0' && s[i] <= '9' )
          {
              //see Jerry's comments for explanation why I do this
              int value = (s[0] == '-') ? ('0' - s[i] ) : (s[i]-'0');
              result = result * 10 + value;
          }
          else
              throw std::invalid_argument("invalid input string");
          i++;
     }
     return result;
} 

由於在上面的代碼中,比較(s[0] == '-')在每次迭代中均完成,因此可以通過在循環中將result計算為負數來避免這種情況,然后在s[0]確實為真的情況下返回result '-' ,否則返回-result (使它成為正數,應該是):

int to_int(char const *s, size_t count)
{
     size_t i = 0 ;
     if ( s[0] == '+' || s[0] == '-' ) 
          ++i;
     int result = 0;
     while(i < count)
     {
          if ( s[i] >= '0' && s[i] <= '9' )
          {
              result = result * 10  - (s[i] - '0');  //assume negative number
          }
          else
              throw std::invalid_argument("invalid input string");
          i++;
     }
     return s[0] == '-' ? result : -result; //-result is positive!
} 

那是一個進步!


但是在C ++ 11中,您可以使用std::stoi系列中的任何函數。 還有std::to_string家族。

llvm::StringRef s(c,sz);
int n;
s.getAsInteger(10,n);
return n;

http://llvm.org/docs/doxygen/html/classllvm_1_1StringRef.html

如果您經常運行該函數,我敢打賭您會多次解析相同的數字。 我的建議是在少於X個數字(32位查找時,X = 8,X = 8)時,將字符串BCD編碼到靜態char緩沖區中(您知道這不會太長,因為atoi只能處理+ -2G)。 16(對於64位查找),然后將緩存放入哈希映射。

完成第一個版本后,您可能會發現不錯的優化方法,例如完全跳過BCD編碼,僅在字符串中使用X個字符(當字符串的長度<= X時)在哈希表中查找。 如果字符串較長,則回atoi

編輯 :...或回退,而不是傑瑞·科芬(Jerry Coffin)解決方案的解決方案,該解決方案很快就可以解決。

如果您死於避免字符串復制,則必須編寫自定義例程或使用3rd party庫。

您可能不希望從頭開始編寫atoi(仍然可能在這里犯一個錯誤),所以我建議您從公共領域或BSD許可的代碼中獲取現有的atoi並進行修改。 例如,您可以從FreeBSD cvs tree獲取現有的atoi。

暫無
暫無

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

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