web-dev-qa-db-ja.com

C ++で文字列をintに解析する方法は?

文字列(char *として与えられる)をintに解析するC++の方法は何ですか?堅牢で明確なエラー処理はプラスです( ゼロを返す の代わり)。

253
Eugene Yokota

新しいC++ 11には、そのための関数があります:stoi、stol、stoll、stoulなど。

int myNr = std::stoi(myString);

変換エラー時に例外をスローします。

これらの新しい関数でさえ、Danが指摘したように同じ問題を持っています:文字列 "11x"を整数 "11"に喜んで変換します。

詳細を参照してください: http://en.cppreference.com/w/cpp/string/basic_string/stol

154
CC.

してはいけないこと

これが私の最初のアドバイスです:thisにはstringstreamを使用しないでください。最初は使いやすいように見えますが、堅牢性と適切なエラー処理が必要な場合は、多くの追加作業が必要になることがわかります。

直感的に機能するように思われるアプローチを次に示します。

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

これには大きな問題があります:str2int(i, "1337h4x0r")は喜んでtrueを返し、iは値1337を取得します。変換後にstringstreamにもう文字がないことを確認することで、この問題を回避できます。

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;
}

1つの問題を修正しましたが、他にもいくつかの問題があります。

文字列の数値が10を基数としない場合はどうなりますか?変換を試みる前に、ストリームを正しいモード(ss << std::hexなど)に設定することにより、他のベースに対応することができます。しかし、これは、発信者が先験的に事前に知っている必要があることを意味します番号の基数は何ですか?発信者は、その番号がまだわかりません。彼らはそれが数字であることさえ知らない!彼らはそれがどのような基盤であるかをどのように知ることが期待されますかプログラムに入力するすべての数値は10を基数とし、16進数または8進数の入力を無効として拒否することを義務付けることができます。しかし、それはあまり柔軟でも堅牢でもありません。この問題に対する簡単な解決策はありません。 10進数の変換は常に8進数(先行ゼロ付き)で成功し、8進数の変換は一部の10進数で成功する可能性があるため、単純に各ベースに対して1回だけ変換を試みることはできません。そのため、今度は先行ゼロを確認する必要があります。ちょっと待って! 16進数も先行ゼロ(0x ...)で開始できます。ため息。

上記の問題に対処できたとしても、さらに別の大きな問題があります。呼び出し側が不正な入力(例: "123foo")とintの範囲外の数(例: "4000000000"を区別する必要がある場合32ビットintの場合)? stringstreamでは、この区別をする方法はありません。変換が成功したか失敗したかのみがわかります。失敗した場合、失敗した理由を知る方法がありません。ご覧のように、stringstreamには、堅牢性と明確なエラー処理が必要な場合に必要なものが多く残されています。

これは私の2番目のアドバイスにつながります:これにはBoostのlexical_castを使用しないでくださいlexical_castのドキュメントの内容を考慮してください:

変換に対して高度な制御が必要な場合、std :: stringstreamおよびstd :: wstringstreamはより適切なパスを提供します。非ストリームベースの変換が必要な場合、lexical_castはジョブにとって不適切なツールであり、そのようなシナリオには特別なケースではありません。

何?? stringstreamの制御レベルが低いことは既に確認しましたが、「より高いレベルの制御」が必要な場合は、lexical_castの代わりにstringstreamを使用する必要があります。また、lexical_caststringstreamの単なるラッパーであるため、stringstreamと同じ問題があります。複数の数値ベースのサポートが不十分で、エラー処理が不十分です。

最適なソリューション

幸いなことに、誰かが上記の問題のすべてをすでに解決しています。 C標準ライブラリには、これらの問題のないstrtolおよびファミリが含まれています。

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;
}

すべてのエラーケースを処理し、2から36までの任意の基数をサポートするものにとっては非常に簡単です。baseがゼロ(デフォルト)の場合、任意の基数からの変換を試みます。または、呼び出し元は3番目の引数を指定して、特定のベースに対してのみ変換を試行するように指定できます。堅牢であり、最小限の労力ですべてのエラーを処理します。

strtol(およびファミリー)を好むその他の理由:

  • はるかに優れた 実行時パフォーマンス
  • コンパイル時のオーバーヘッドが少なくなります(他のものはヘッダーからほぼ20倍のSLOCを取り込みます)
  • コードサイズが最小になります

他の方法を使用する正当な理由はまったくありません。

197
Dan Moulding

これは、atoi()より安全なCの方法です。

const char* str = "123";
int i;

if(sscanf(str, "%d", &i)  == EOF )
{
   /* error */
}

C++と標準ライブラリ stringstream :(ありがとう CMS

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  { 
      //ERROR 
  }
  return num;
}

boost library:(thanks jk

#include <boost/lexical_cast.hpp>
#include <string>

try
{
    std::string str = "123";
    int number = boost::lexical_cast< int >( str );
}
catch( const boost::bad_lexical_cast & )
{
    // Error
}

編集:エラーを処理するようにstringstreamバージョンを修正しました。 (元の投稿に対するCMSとjkのコメントのおかげ)

67
Luka Marinko

Boostのlexical_cast を使用できます。これは、より一般的なインターフェイスで これをラップ します。 lexical_cast<Target>(Source)は失敗するとbad_lexical_castをスローします。

22
jk.

良い '古いCの方法はまだ動作します。 strtolまたはstrtoulをお勧めします。戻りステータスと「endPtr」の間で、適切な診断出力を提供できます。また、複数のベースをうまく処理します。

21
Chris Arguin

C++標準ライブラリの文字列ストリームを使用できます。

stringstream ss(str);
int x;
ss >> x;

if(ss) { // <-- error handling
  // use x
} else {
  // not a number
}

整数を読み取ろうとしたときに数字以外が検出されると、ストリームの状態は失敗に設定されます。

C++でのエラー処理とストリームの落とし穴については、 ストリームの落とし穴 を参照してください。

16
jk.

stringstream's を使用できます

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}
10
CMS

次の3つのリンクがそれを要約すると思います。

stringstreamおよびlexical_castソリューションは、字句キャストがstringstreamを使用しているのとほぼ同じです。

字句キャストの一部の特殊化では、異なるアプローチを使用しますhttp://www.boost.org/doc/libs/release/boost/lexical_cast.hpp詳細については。整数とフロートは、整数から文字列への変換に特化されました。

Lexical_castを自分のニーズに合わせて特化し、高速にすることができます。これはすべての関係者を満足させる究極のソリューションであり、クリーンでシンプルです。

既に述べた記事は、整数<->文字列を変換するさまざまな方法の比較を示しています。次のアプローチは理にかなっています:古いc-way、spirit.karma、fastformat、単純な単純ループ。

Lexical_castは、場合によっては大丈夫です。 intから文字列への変換用。

字句キャストを使用して文字列をintに変換することは、使用するプラットフォーム/コンパイラによってはatoiよりも10〜40倍遅いため、お勧めできません。

Boost.Spirit.Karmaは、整数を文字列に変換するための最速のライブラリのようです。

ex.: generate(ptr_char, int_, integer_number);

上記の記事の基本的な単純なループは、文字列をintに変換する最も速い方法であり、明らかに最も安全ではありません。strtol()はより安全なソリューションのようです

int naive_char_2_int(const char *p) {
    int x = 0;
    bool neg = false;
    if (*p == '-') {
        neg = true;
        ++p;
    }
    while (*p >= '0' && *p <= '9') {
        x = (x*10) + (*p - '0');
        ++p;
    }
    if (neg) {
        x = -x;
    }
    return x;
}
7
caa

C++ String Toolkit Library(StrTk) には次の解決策があります。

static const std::size_t digit_table_symbol_count = 256;
static const unsigned char digit_table[digit_table_symbol_count] = {
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF - 0x07
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08 - 0x0F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 - 0x17
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18 - 0x1F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 - 0x27
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x28 - 0x2F
   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
   0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38 - 0x3F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40 - 0x47
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x48 - 0x4F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x50 - 0x57
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58 - 0x5F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60 - 0x67
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x68 - 0x6F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x70 - 0x77
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78 - 0x7F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x80 - 0x87
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x88 - 0x8F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x90 - 0x97
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x98 - 0x9F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA0 - 0xA7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA8 - 0xAF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB0 - 0xB7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB8 - 0xBF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC0 - 0xC7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC8 - 0xCF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD0 - 0xD7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD8 - 0xDF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0 - 0xE7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE8 - 0xEF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xF0 - 0xF7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 0xF8 - 0xFF
 };

template<typename InputIterator, typename T>
inline bool string_to_signed_type_converter_impl_itr(InputIterator begin, InputIterator end, T& v)
{
   if (0 == std::distance(begin,end))
      return false;
   v = 0;
   InputIterator it = begin;
   bool negative = false;
   if ('+' == *it)
      ++it;
   else if ('-' == *it)
   {
      ++it;
      negative = true;
   }
   if (end == it)
      return false;
   while(end != it)
   {
      const T digit = static_cast<T>(digit_table[static_cast<unsigned int>(*it++)]);
      if (0xFF == digit)
         return false;
      v = (10 * v) + digit;
   }
   if (negative)
      v *= -1;
   return true;
}

InputIteratorは、unsigned char *、char *、またはstd :: stringイテレーターのいずれかであり、Tはsigned int、int、またはlongなどの符号付きintであることが期待されます。

7
Matthieu N.

C++ 11を使用している場合、現在適切なソリューションは、<string>のC++整数変換関数:stoistolstoulstollstoullです。不正な入力が与えられると適切な例外をスローし、内部で高速で小さなstrto*関数を使用します。

C++の以前のリビジョンに固執している場合、実装でこれらの機能を模倣することは、将来の移植性に優れています。

6
fuzzyTew

C++ 17以降では、文書化されている here のようにstd::from_charsヘッダーの<charconv>を使用できます。

例えば:

#include <iostream>
#include <charconv>
#include <array>

int main()
{
    char const * str = "42";
    int value = 0;

    std::from_chars_result result = std::from_chars(std::begin(str), std::end(str), value);

    if(result.error == std::errc::invalid_argument)
    {
      std::cout << "Error, invalid format";
    }
    else if(result.error == std::errc::result_out_of_range)
    {
      std::cout << "Error, value too big for int range";
    }
    else
    {
      std::cout << "Success: " << result;
    }
}

ボーナスとして、16進数などの他のベースも処理できます。

5
Pharap

Dan Moulding's answer が好きです。C++スタイルを少し追加します。

#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

int to_int(const std::string &s, int base = 0)
{
    char *end;
    errno = 0;
    long result = std::strtol(s.c_str(), &end, base);
    if (errno == ERANGE || result > INT_MAX || result < INT_MIN)
        throw std::out_of_range("toint: string is out of range");
    if (s.length() == 0 || *end != '\0')
        throw std::invalid_argument("toint: invalid string");
    return result;
}

暗黙的な変換により、std :: stringとconst char *の両方で機能します。また、ベース変換にも役立ちます。すべてのto_int("0x7b")to_int("0173")to_int("01111011", 2)to_int("0000007B", 16)to_int("11120", 3)to_int("3L", 34);は123を返します。

std::stoiとは異なり、C++ 11より前で動作します。また、std::stoiboost::lexical_cast、およびstringstreamとは異なり、「123hohoho」のような奇妙な文字列に対して例外をスローします。

注意:この関数は、先頭のスペースを許容しますが、末尾のスペースは許容しません。つまり、to_int(" 123")は例外をスローしながら、to_int("123 ")は123を返します。これがユースケースに受け入れられることを確認するか、コードを調整してください。

このような機能はSTLの一部である可能性があります...

2
user3925906

Stringをintに変換する3つの方法を知っています。

Stoi(String to int)関数を使用するか、個別の変換を実行する3番目の方法であるStringstreamを使用します。コードは以下のとおりです。

第1の方法

std::string s1 = "4533";
std::string s2 = "3.010101";
std::string s3 = "31337 with some string";

int myint1 = std::stoi(s1);
int myint2 = std::stoi(s2);
int myint3 = std::stoi(s3);

std::cout <<  s1 <<"=" << myint1 << '\n';
std::cout <<  s2 <<"=" << myint2 << '\n';
std::cout <<  s3 <<"=" << myint3 << '\n';

2番目の方法

#include <string.h>
#include <sstream>
#include <iostream>
#include <cstring>
using namespace std;


int StringToInteger(string NumberAsString)
{
    int NumberAsInteger;
    stringstream ss;
    ss << NumberAsString;
    ss >> NumberAsInteger;
    return NumberAsInteger;
}
int main()
{
    string NumberAsString;
    cin >> NumberAsString;
    cout << StringToInteger(NumberAsString) << endl;
    return 0;
} 

番目の方法-ただし、個々の変換用ではない

std::string str4 = "453";
int i = 0, in=0; // 453 as on
for ( i = 0; i < str4.length(); i++)
{

    in = str4[i];
    cout <<in-48 ;

}
2
Iqra.

私は ダンの答え が好きです、特に例外を避けるためです。組み込みシステム開発およびその他の低レベルシステム開発では、適切な例外フレームワークが利用できない場合があります。

有効な文字列の後に空白のチェックを追加しました...これらの3行

    while (isspace(*end)) {
        end++;
    }


解析エラーのチェックも追加しました。

    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }


これが完全な機能です。

#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

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

STR2INT_ERROR str2long (long &l, char const *s, int base = 0)
{
    char *end = (char *)s;
    errno = 0;

    l = strtol(s, &end, base);

    if ((errno == ERANGE) && (l == LONG_MAX)) {
        return OVERFLOW;
    }
    if ((errno == ERANGE) && (l == LONG_MIN)) {
        return UNDERFLOW;
    }
    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }    
    while (isspace((unsigned char)*end)) {
        end++;
    }

    if (*s == '\0' || *end != '\0') {
        return INCONVERTIBLE;
    }

    return SUCCESS;
}
1
pellucide

私はこれが古い質問であることを知っていますが、私は何度もこの問題に出くわしましたが、今までのところ、次の特徴を備えた適切にテンプレート化されたソリューションは見つかりませんでした:

  • 任意のベースを変換できます(ベースタイプを検出します)
  • エラーのあるデータを検出します(つまり、先頭/末尾の空白が少ない文字列全体が変換によって消費されるようにします)
  • 変換された型に関係なく、文字列の値の範囲が受け入れられることを保証します。

だから、ここに私のもの、テストストラップがあります。内部でC関数strtoull/strtollを使用するため、常に最初に利用可能な最大の型に変換します。次に、最大のタイプを使用していない場合、追加の範囲チェックを実行して、タイプがオーバーフロー(アンダー)フローしていないことを確認します。このため、strtol/strtoulを適切に選択した場合よりもパフォーマンスが少し低下します。ただし、これはshort/charsでも機能し、私の知る限り、それを行う標準ライブラリ関数も存在しません。

楽しい;うまくいけば、誰かがそれを役に立つと思う。

#include <cstdlib>
#include <cerrno>
#include <limits>
#include <stdexcept>
#include <sstream>

static const int DefaultBase = 10;

template<typename T>
static inline T CstrtoxllWrapper(const char *str, int base = DefaultBase)
{
    while (isspace(*str)) str++; // remove leading spaces; verify there's data
    if (*str == '\0') { throw std::invalid_argument("str; no data"); } // nothing to convert

    // NOTE:  for some reason strtoull allows a negative sign, we don't; if
    //          converting to an unsigned then it must always be positive!
    if (!std::numeric_limits<T>::is_signed && *str == '-')
    { throw std::invalid_argument("str; negative"); }

    // reset errno and call fn (either strtoll or strtoull)
    errno = 0;
    char *ePtr;
    T tmp = std::numeric_limits<T>::is_signed ? strtoll(str, &ePtr, base)
                                              : strtoull(str, &ePtr, base);

    // check for any C errors -- note these are range errors on T, which may
    //   still be out of the range of the actual type we're using; the caller
    //   may need to perform additional range checks.
    if (errno != 0) 
    {
            if (errno == ERANGE) { throw std::range_error("str; out of range"); }
            else if (errno == EINVAL) { throw std::invalid_argument("str; EINVAL"); }
            else { throw std::invalid_argument("str; unknown errno"); }
    }

    // verify everything converted -- extraneous spaces are allowed
    if (ePtr != NULL)
    {
            while (isspace(*ePtr)) ePtr++;
            if (*ePtr != '\0') { throw std::invalid_argument("str; bad data"); }
    }

    return tmp;
}

template<typename T>
T StringToSigned(const char *str, int base = DefaultBase)
{
    static const long long max = std::numeric_limits<T>::max();
    static const long long min = std::numeric_limits<T>::min();

    long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp < min || tmp > max)
    {
            std::ostringstream err;
            err << "str; value " << tmp << " out of " << sizeof(T) * 8
                << "-bit signed range (";
            if (sizeof(T) != 1) err << min << ".." << max;
            else err << (int) min << ".." << (int) max;  // don't print garbage chars
            err << ")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
T StringToUnsigned(const char *str, int base = DefaultBase)
{
    static const unsigned long long max = std::numeric_limits<T>::max();

    unsigned long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp > max)
    {
            std::ostringstream err;
            err << "str; value " << tmp << " out of " << sizeof(T) * 8
                << "-bit unsigned range (0..";
            if (sizeof(T) != 1) err << max;
            else err << (int) max;  // don't print garbage chars
            err << ")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
inline T
StringToDecimal(const char *str, int base = DefaultBase)
{
    return std::numeric_limits<T>::is_signed ? StringToSigned<T>(str, base)
                                             : StringToUnsigned<T>(str, base);
}

template<typename T>
inline T
StringToDecimal(T &out_convertedVal, const char *str, int base = DefaultBase)
{
    return out_convertedVal = StringToDecimal<T>(str, base);
}

/*============================== [ Test Strap ] ==============================*/ 

#include <inttypes.h>
#include <iostream>

static bool _g_anyFailed = false;

template<typename T>
void TestIt(const char *tName,
            const char *s, int base,
            bool successExpected = false, T expectedValue = 0)
{
    #define FAIL(s) { _g_anyFailed = true; std::cout << s; }

    T x;
    std::cout << "converting<" << tName << ">b:" << base << " [" << s << "]";
    try
    {
            StringToDecimal<T>(x, s, base);
            // get here on success only
            if (!successExpected)
            {
                    FAIL(" -- TEST FAILED; SUCCESS NOT EXPECTED!" << std::endl);
            }
            else
            {
                    std::cout << " -> ";
                    if (sizeof(T) != 1) std::cout << x;
                    else std::cout << (int) x;  // don't print garbage chars
                    if (x != expectedValue)
                    {
                            FAIL("; FAILED (expected value:" << expectedValue << ")!");
                    }
                    std::cout << std::endl;
            }
    }
    catch (std::exception &e)
    {
            if (successExpected)
            {
                    FAIL(   " -- TEST FAILED; EXPECTED SUCCESS!"
                         << " (got:" << e.what() << ")" << std::endl);
            }
            else
            {
                    std::cout << "; expected exception encounterd: [" << e.what() << "]" << std::endl;
            }
    }
}

#define TEST(t, s, ...) \
    TestIt<t>(#t, s, __VA_ARGS__);

int main()
{
    std::cout << "============ variable base tests ============" << std::endl;
    TEST(int, "-0xF", 0, true, -0xF);
    TEST(int, "+0xF", 0, true, 0xF);
    TEST(int, "0xF", 0, true, 0xF);
    TEST(int, "-010", 0, true, -010);
    TEST(int, "+010", 0, true, 010);
    TEST(int, "010", 0, true, 010);
    TEST(int, "-10", 0, true, -10);
    TEST(int, "+10", 0, true, 10);
    TEST(int, "10", 0, true, 10);

    std::cout << "============ base-10 tests ============" << std::endl;
    TEST(int, "-010", 10, true, -10);
    TEST(int, "+010", 10, true, 10);
    TEST(int, "010", 10, true, 10);
    TEST(int, "-10", 10, true, -10);
    TEST(int, "+10", 10, true, 10);
    TEST(int, "10", 10, true, 10);
    TEST(int, "00010", 10, true, 10);

    std::cout << "============ base-8 tests ============" << std::endl;
    TEST(int, "777", 8, true, 0777);
    TEST(int, "-0111 ", 8, true, -0111);
    TEST(int, "+0010 ", 8, true, 010);

    std::cout << "============ base-16 tests ============" << std::endl;
    TEST(int, "DEAD", 16, true, 0xDEAD);
    TEST(int, "-BEEF", 16, true, -0xBEEF);
    TEST(int, "+C30", 16, true, 0xC30);

    std::cout << "============ base-2 tests ============" << std::endl;
    TEST(int, "-10011001", 2, true, -153);
    TEST(int, "10011001", 2, true, 153);

    std::cout << "============ irregular base tests ============" << std::endl;
    TEST(int, "Z", 36, true, 35);
    TEST(int, "ZZTOP", 36, true, 60457993);
    TEST(int, "G", 17, true, 16);
    TEST(int, "H", 17);

    std::cout << "============ space deliminated tests ============" << std::endl;
    TEST(int, "1337    ", 10, true, 1337);
    TEST(int, "   FEAD", 16, true, 0xFEAD);
    TEST(int, "   0711   ", 0, true, 0711);

    std::cout << "============ bad data tests ============" << std::endl;
    TEST(int, "FEAD", 10);
    TEST(int, "1234 asdfklj", 10);
    TEST(int, "-0xF", 10);
    TEST(int, "+0xF", 10);
    TEST(int, "0xF", 10);
    TEST(int, "-F", 10);
    TEST(int, "+F", 10);
    TEST(int, "12.4", 10);
    TEST(int, "ABG", 16);
    TEST(int, "10011002", 2);

    std::cout << "============ int8_t range tests ============" << std::endl;
    TEST(int8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(int8_t, "80", 16);
    TEST(int8_t, "-80", 16, true, std::numeric_limits<int8_t>::min());
    TEST(int8_t, "-81", 16);
    TEST(int8_t, "FF", 16);
    TEST(int8_t, "100", 16);

    std::cout << "============ uint8_t range tests ============" << std::endl;
    TEST(uint8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(uint8_t, "80", 16, true, std::numeric_limits<int8_t>::max()+1);
    TEST(uint8_t, "-80", 16);
    TEST(uint8_t, "-81", 16);
    TEST(uint8_t, "FF", 16, true, std::numeric_limits<uint8_t>::max());
    TEST(uint8_t, "100", 16);

    std::cout << "============ int16_t range tests ============" << std::endl;
    TEST(int16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(int16_t, "8000", 16);
    TEST(int16_t, "-8000", 16, true, std::numeric_limits<int16_t>::min());
    TEST(int16_t, "-8001", 16);
    TEST(int16_t, "FFFF", 16);
    TEST(int16_t, "10000", 16);

    std::cout << "============ uint16_t range tests ============" << std::endl;
    TEST(uint16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(uint16_t, "8000", 16, true, std::numeric_limits<int16_t>::max()+1);
    TEST(uint16_t, "-8000", 16);
    TEST(uint16_t, "-8001", 16);
    TEST(uint16_t, "FFFF", 16, true, std::numeric_limits<uint16_t>::max());
    TEST(uint16_t, "10000", 16);

    std::cout << "============ int32_t range tests ============" << std::endl;
    TEST(int32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(int32_t, "80000000", 16);
    TEST(int32_t, "-80000000", 16, true, std::numeric_limits<int32_t>::min());
    TEST(int32_t, "-80000001", 16);
    TEST(int32_t, "FFFFFFFF", 16);
    TEST(int32_t, "100000000", 16);

    std::cout << "============ uint32_t range tests ============" << std::endl;
    TEST(uint32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(uint32_t, "80000000", 16, true, std::numeric_limits<int32_t>::max()+1);
    TEST(uint32_t, "-80000000", 16);
    TEST(uint32_t, "-80000001", 16);
    TEST(uint32_t, "FFFFFFFF", 16, true, std::numeric_limits<uint32_t>::max());
    TEST(uint32_t, "100000000", 16);

    std::cout << "============ int64_t range tests ============" << std::endl;
    TEST(int64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(int64_t, "8000000000000000", 16);
    TEST(int64_t, "-8000000000000000", 16, true, std::numeric_limits<int64_t>::min());
    TEST(int64_t, "-8000000000000001", 16);
    TEST(int64_t, "FFFFFFFFFFFFFFFF", 16);
    TEST(int64_t, "10000000000000000", 16);

    std::cout << "============ uint64_t range tests ============" << std::endl;
    TEST(uint64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(uint64_t, "8000000000000000", 16, true, std::numeric_limits<int64_t>::max()+1);
    TEST(uint64_t, "-8000000000000000", 16);
    TEST(uint64_t, "-8000000000000001", 16);
    TEST(uint64_t, "FFFFFFFFFFFFFFFF", 16, true, std::numeric_limits<uint64_t>::max());
    TEST(uint64_t, "10000000000000000", 16);

    std::cout << std::endl << std::endl
              << (_g_anyFailed ? "!! SOME TESTS FAILED !!" : "ALL TESTS PASSED")
              << std::endl;

    return _g_anyFailed;
}

StringToDecimalはユーザーランドのメソッドです。オーバーロードされているため、次のように呼び出すことができます。

int a; a = StringToDecimal<int>("100");

またはこれ:

int a; StringToDecimal(a, "100");

私はint型を繰り返すのが嫌いなので、後者を好む。これにより、「a」のタイプが変更されても、悪い結果が得られないことが保証されます。コンパイラが次のように理解できることを望みます。

int a; a = StringToDecimal("100");

...しかし、C++はテンプレートの戻り値の型を推測しません。

実装は非常に簡単です。

CstrtoxllWrapperは、strtoullstrtollの両方をラップし、テンプレートタイプの符号付きに基づいて必要な方を呼び出し、追加の保証を提供します(たとえば、符号なしの場合、負の入力は許可されず、文字列全体が変換されます)。

CstrtoxllWrapperは、コンパイラで使用可能な最大の型(long long/unsigned long long)を持つStringToSignedおよびStringToUnsignedによって使用されます。これにより、最大の変換を実行できます。次に、必要に応じて、StringToSigned/StringToUnsignedが基になる型の最終的な範囲チェックを実行します。最後に、エンドポイントメソッドStringToDecimalは、基になる型の符号付きに基づいて、どのStringTo *テンプレートメソッドを呼び出すかを決定します。

ほとんどのジャンクはコンパイラによって最適化できると思います。ほぼすべてがコンパイル時に決定的である必要があります。この点に関するコメントは、私にとって興味深いものです!

0
DreamWarrior

この定義されたメソッドを使用できます。

#define toInt(x) {atoi(x.c_str())};

また、文字列から整数に変換する場合は、次のようにします。

int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}

出力は102になります。

0
Boris