www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Phobos addition - std.conv.toInt(char[] s, int base)

reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
I've been missing the ability to convert any base of string numerical 
literal into an int (i.e. strtol()), so I wrote a new toInt that allows any 
base from 2 to 36 (bases greater than 10 use a-z, case-insensitive).  It 
accomplishes this by using a translation table (generated by 
std.string.maketrans() of course) for easy parsing.  It's written pretty 
much the same way that the existing toInt is.

int toInt(char[] s, int base)
{
 assert(base >= 2 && base <= 36);

 static char[] transTable =
 [
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  64, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
  73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 91, 92, 93, 94, 95,
  96, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
  73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 123, 124, 125, 126,
  127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
  140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
  153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
  166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
  179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
  192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
  205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
  218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
  231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
  244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
 ];

    int length = s.length;

 if(!length)
  throw new ConvError(s);

 int sign = 0;
 int v = 0;

 char maxDigit = '0' + base - 1;

 for(int i = 0; i < length; i++)
 {
  char c = transTable[s[i]];

  if(c >= '0' && c <= maxDigit)
  {
   uint v1 = v;
   v = v * base + (c - '0');

   if(cast(uint)v < v1)
    throw new ConvOverflowError(s);
  }
  else if(c == '-' && i == 0)
  {
   sign = -1;

   if(length == 1)
    throw new ConvError(s);
  }
  else if(c == '+' && i == 0)
  {
   if(length == 1)
    throw new ConvError(s);
  }
  else
   throw new ConvError(s);
 }

 if(sign == -1)
 {
  if(cast(uint)v > 0x80000000)
   throw new ConvOverflowError(s);

  v = -v;
 }
 else
 {
  if(cast(uint)v > 0x7FFFFFFF)
   throw new ConvOverflowError(s);
 }

 return v;
} 
May 29 2006
parent reply "Bob W" <nospam aol.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:e5gcr4$27r5$1 digitaldaemon.com...
 I've been missing the ability to convert any base of string numerical 
 literal into an int (i.e. strtol()), so I wrote a new toInt that allows 
 any base from 2 to 36 (bases greater than 10 use a-z, case-insensitive). 
 It accomplishes this by using a translation table (generated by 
 std.string.maketrans() of course) for easy parsing.  It's written pretty 
 much the same way that the existing toInt is.

Unless you specifically intend to get identical results for e. g. toInt(";:=",16) and toInt("BAD",16) , you need to exclude a certain character range from conversion. Have a look at this one: ------------ int toInt(char[] s, int base) { const char XX=0xff; const char[256] transTable = [ XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX,XX,XX,XX,XX, XX,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, 25,26,27,28,29,30,31,32,33,34,35,26,XX,XX,XX,XX, XX,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, 25,26,27,28,29,30,31,32,33,34,35,26,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX, XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX ]; assert (base>0 && base<37); int length = s.length; if (!length) throw new ConvError(s); uint minus = 0, v = 0; foreach (i, c; s) { char ct=transTable[c]; if (ct<base) { uint v1 = v; v = v*base + ct; if (v<v1) throw new ConvOverflowError(s); } else if (!i) { if (c == '-') minus=1; else if (c != '+') throw new ConvError(s); } else throw new ConvError(s); } if (v & 0x80000000) { if (!minus) throw new ConvOverflowError(s); if (v & 0x7fffffff) throw new ConvOverflowError(s); } return cast(int)(minus ? 0-v : v); } ----------------- Please note that my sample program accepts a base of 1. This is still a valid base but it probably has no practical merits.
May 30 2006
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Bob W" <nospam aol.com> wrote in message 
news:e5h97s$muj$1 digitaldaemon.com...

 Unless you specifically intend to get identical results
 for e. g.   toInt(";:=",16)   and   toInt("BAD",16) ,
 you need to exclude a certain character range from
 conversion.

Ooh, how insidious. Thanks for catching that.
May 30 2006