SORU
11 EKİM 2008, CUMARTESİ


Nasıl C bir tamsayı bir dize ayrıştırmak için ?

Bir int içine bir string (char * olarak verilen) ayrıştırma C yolu nedir? Sağlam ve net hata işleme bir artı (yerine returning zero).

CEVAP
27 Mayıs 2011, Cuma


Ne yapmak değil

Burada ilk tavsiyem:bunun için stringstream kullanmayın. İlk başta kullanmak için basit görünse de, eğer sağlamlık ve iyi hata istiyorsanız ekstra bir sürü iş yapmak zorunda bulabilirsiniz.

Burada sezgisel olarak çalışması gerekir gibi görünen bir yaklaşım

bool str2int (int &i, char const *s)
{
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail()) {
        // not an integer
        return false;
    }
    return true;
}

Bu büyük bir sorun vardır: str2int(i, "1337h4x0r") mutlu true i değeri 1337. döndürür Dönüşüm sonra stringstream Daha fazla karakter var sağlayarak bu soruna geçici bir çözüm bulabiliriz:

bool str2int (int &i, char const *s)
{
    char              c;
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail() || ss.get(c)) {
        // not an integer
        return false;
    }
    return true;
}

Tek sorun, sabit, ama yine de diğer sorunların bir çift vardır.

Eğer dize sayısı 10 tabanına değilse ne? Dönüşüm denemeden önce doğru modu (ss << std::hex gibi) akışı ayarlayarak başka noktalara yerleştirmek için deneyebilirsiniz. Ama bu arayanın bilmeniz gerekir anlamına gelirönselnumarasını ... ne adi ve ne arayan muhtemelen bunu bilebilir mi? Arayanın numarasını henüz ne olduğunu bilmiyor. Hatta onlar bunu bilmiyorbir sayı! Nasıl olduğunu bilmek ne beklenir? Sadece bütün numaraları programlarımız için giriş 10 tabanına olması gereken yetki ve geçersiz olarak onaltılık veya sekizli giriş reddetme. Ama o çok esnek ya da güçlü değildir. Bu sorunun basit bir çözümü yok. Sadece ondalık dönüşüm her zaman sekizlik sayılar için başarısız olur, çünkü dönüşüm sonra her baz için deneyebilirsiniz. (sıfır) ve sekizli dönüştürme bazı ondalık sayılar için başarısız olabilir. Şimdi sıfır için kontrol etmelisiniz. Ama bekleyin! Onaltılı sayıların önünde sıfır ile de başlayabilir (0...). İç çekiş.

Hatta eğer başarılı uğraşan yukarıdaki sorunlar, hala var, başka bir büyük sorun: ne olursa arayanın ihtiyaçlarına ayırt kötü bir giriş (örneğin, "123foo") ve bir dizi çıkış aralığı int (örneğin "4000000000" için 32-bit int)? stringstream ile bu ayrımı yapmak için bir yol yoktur. Biz sadece dönüştürme başarılı veya başarısız olup olmadığını biliyor. Eğer başarısız olursa, bilmenin bir yolu varnedenbu başarısız oldu. Gördüğünüz gibi, stringstream Eğer isterseniz, istenen çok şey bırakır ve hata işleme sağlamlık temizleyin.

Bu ikinci tavsiyem ise beni götürür:faydası yok bunun için lexical_cast Destek. lexical_cast belgelere söylediklerini düşünün:

Kontrol daha yüksek bir derece olduğu dönüşümler üzerinde gerekli, ::std stringstream ve ::std bir daha wstringstream sunuyoruz uygun yol. Nerede non-stream tabanlı bir hadise gerekli lexical_cast yanlış. bu iş için aracı değildir bu tür senaryolar için özel kasalı.

Ne?? Biz zaten görülen o stringstream bir zavallı seviye kontrolü, ve yine diyor ki stringstream yerine kullanılmalıdır lexical_cast ihtiyacın olursa "daha yüksek bir seviyeye kontrol". Ayrıca, lexical_cast stringstream, etrafında sarıcı çünkü stringstream aynı sorunları muzdarip: birden çok sayıda üsleri ve zavallı hata işleme için düşük destek.

En iyi çözüm

Neyse ki, birisi zaten yukarıdaki tüm sorunları çözmüştür. C standart kütüphane bu sorunların hiçbiri strtol ve aile içerir.

enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };

STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
    char *end;
    long  l;
    errno = 0;
    l = strtol(s, &end, base);
    if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
        return OVERFLOW;
    }
    if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
        return UNDERFLOW;
    }
    if (*s == '\0' || *end != '\0') {
        return INCONVERTIBLE;
    }
    i = l;
    return SUCCESS;
}

Tüm hata davalara bakan ve aynı zamanda herhangi bir sayıda destekleyen bir şey 36 2 Temel için oldukça basit. Eğer base sıfır (varsayılan) herhangi bir temel dönüştürmek için çalışacağız. Ya arayan üçüncü argümanın ve dönüşüm sadece belirli bir temel için denenmesi gerektiğini belirtebilirsiniz. Sağlam ve çaba en az miktarda tüm hatalar işler.

Diğer nedenler strtol (ve aile) tercih:

  • Çok iyi runtime performance sergiler
  • Az derleme zamanı Tepegöz (Diğerleri başlıkları arasından yaklaşık 20 kat daha fazla MASRAF çekin) tanıttı
  • En küçük kod boyutu ile sonuçlanır

Kesinlikle başka bir yöntemi kullanmak için iyi bir neden yoktur.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • PremiumBeat.com - Royalty Free Music

    PremiumBeat.

    16 Kasım 2008
  • Troy Hunt

    Troy Hunt

    29 EYLÜL 2011
  • Tutorials Junction

    Tutorials Ju

    1 Ocak 2014