2009年5月15日金曜日

文字列をRubyっぽく数値(整数)変換する

可能な限り数値に変換しようとするのでちょっと挙動は違う。

integer.cpp
namespace Integer {

struct BinaryNumber
{
static const int BASE = 2;
static bool acceptable(char ch)
{
return '0' == ch || '1' == ch;
}
static int to_i(char ch)
{
return ch - '0';
}
};

struct OctalNumber
{
static const int BASE = 8;
static bool acceptable(char ch)
{
return '0' <= ch && ch <= '7';
}
static int to_i(char ch)
{
return ch - '0';
}
};

struct DecimalNumber
{
static const int BASE = 10;
static bool acceptable(char ch)
{
return '0' <= ch && ch <= '9';
}
static int to_i(char ch)
{
return ch - '0';
}
};

struct HexadecimalNumber
{
static const int BASE = 16;
static bool acceptable(char ch)
{
return
('0' <= ch && ch <= '9')
|| ('a' <= ch && ch <= 'f')
|| ('A' <= ch && ch <= 'F');
}
static int to_i(char ch)
{
if('0' <= ch && ch <= '9'){ return ch - '0'; }
if('a' <= ch && ch <= 'f'){ return ch - 'a' + 10; }
return ch - 'A' + 10;
}
};

template<typename BaseNumberTraits>
int parse_last(const char* itr, const char* end)
{
int value = BaseNumberTraits::to_i(*itr);
while(true)
{
++itr;
if(itr == end){ return value; } // OK
if(*itr == '_'){
++itr;
if(itr == end){ return value; } // OK (with garbage)
}
if(!BaseNumberTraits::acceptable(*itr)){ return value; } // OK (with garbage)
value =
value * BaseNumberTraits::BASE
+ BaseNumberTraits::to_i(*itr);
}
}

template<typename BaseNumberTraits>
int parse_first(const char* itr, const char* end)
{
++itr;
if(itr == end){ return 0; } // NG
if(!BaseNumberTraits::acceptable(*itr)){ return 0; } // NG
return parse_last<BaseNumberTraits>(itr, end);
}

int to_i(const char* itr, const char* end)
{
if(itr == end){ return 0; } // NG

int flag = 1;
switch(*itr)
{
case '-':
flag = -1;
// no break needed
case '+':
while(true)
{
++itr;
if(itr == end){ return 0; } // NG
if(*itr != ' '){ break; }
}
break;
}

if(*itr == '0')
{
++itr;
if(itr == end){ return 0; } // OK

if('x' == *itr || 'X' == *itr)
return flag * parse_first<HexadecimalNumber>(itr, end);

if(OctalNumber::acceptable(*itr))
return flag * parse_last<OctalNumber>(itr, end);

if('_' == *itr)
return flag * parse_first<OctalNumber>(itr, end);

if('o' == *itr || 'O' == *itr)
return flag * parse_first<OctalNumber>(itr, end);

if('b' == *itr || 'B' == *itr)
return flag * parse_first<BinaryNumber>(itr, end);

if('d' == *itr || 'D' == *itr)
return flag * parse_first<DecimalNumber>(itr, end);

return 0; // NG
}

if(DecimalNumber::acceptable(*itr))
return flag * parse_last<DecimalNumber>(itr, end);

return 0; // NG
}
}

0 件のコメント:

コメントを投稿