Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 2.03 on page 46

Write the function htoi(s) , which converts a string of hexadecimal digits (including an optional 0x or 0X) into its equivalent integer value. The allowable digits are 0 through 9, a through f, and A through F .

Solutions by Richard Heathfield and Marshall S McLeish and Pilcrow

Here's my solution:

/* Write the function htoi(s), which converts a string of hexadecimal
 * digits (including an optional 0x or 0X) into its equivalent integer
 * value. The allowable digits are 0 through 9, a through f, and
 * A through F.
 *
 * I've tried hard to restrict the solution code to use only what
 * has been presented in the book at this point (page 46). As a
 * result, the implementation may seem a little naive. Error
 * handling is a problem. I chose to adopt atoi's approach, and
 * return 0 on error. Not ideal, but the interface doesn't leave
 * Richard Heathfield much choice.
 *
 * I've used unsigned int to keep the behaviour well-defined even
 * if overflow occurs. After all, the exercise calls for conversion
 * to 'an integer', and unsigned ints are integers!
 */

/* These two header files are only needed for the test driver */
#include <stdio.h>
#include <stdlib.h>

/* Here's a helper function to get Richard Heathfield around the problem of not
 * having strchr
 */

int hexalpha_to_int(int c)
{
  char hexalpha[] = "aAbBcCdDeEfF";
  int i;
  int answer = 0;

  for(i = 0; answer == 0 && hexalpha[i] != '\0'; i++)
  {
    if(hexalpha[i] == c)
    {
      answer = 10 + (i / 2);
    }
  }

  return answer;
}

unsigned int htoi(const char s[])
{
  unsigned int answer = 0;
  int i = 0;
  int valid = 1;
  int hexit;

  if(s[i] == '0')
  {
    ++i;
    if(s[i] == 'x' || s[i] == 'X')
    {
      ++i;
    }
  }

  while(valid && s[i] != '\0')
  {
    answer = answer * 16;
    if(s[i] >= '0' && s[i] <= '9')
    {
      answer = answer + (s[i] - '0');
    }
    else
    {
      hexit = hexalpha_to_int(s[i]);
      if(hexit == 0)
      {
        valid = 0;
      }
      else
      {
        answer = answer + hexit;
      }
    }

    ++i;
  }

  if(!valid)
  {
    answer = 0;
  }

  return answer;
}

/* Solution finished. This bit's just a test driver, so
 * I've relaxed the rules on what's allowed.
 */

int main(void)
{
  char *endp = NULL;
  char *test[] =
  {
    "F00",
    "bar",
    "0100",
    "0x1",
    "0XA",
    "0X0C0BE",
    "abcdef",
    "123456",
    "0x123456",
    "deadbeef",
    "zog_c"
  };

  unsigned int result;
  unsigned int check;

  size_t numtests = sizeof test / sizeof test[0];

  size_t thistest;

  for(thistest = 0; thistest < numtests; thistest++)
  {
    result = htoi(test[thistest]);
    check = (unsigned int)strtoul(test[thistest], &endp, 16);

    if((*endp != '\0' && result == 0) || result == check)
    {
      printf("Testing %s. Correct. %u\n", test[thistest], result);
    }
    else
    {
      printf("Testing %s. Incorrect. %u\n", test[thistest], result);
    }
  }

  return 0;
}




And here's Marshall's:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

long hchartoi (char hexdig, int pos);   /* converts a hex char to decimal knowing its 0 based place value */
long htoi (char hexstring[]);               /* converts a string of hex bits to integer ... */

int main(void)
{
  char *endp = NULL;
  char *test[] =
  {
    "F00",
    "bar",
    "0100",
    "0x1",
    "0XA",
    "0X0C0BE",
    "abcdef",
    "123456",
    "0x123456",
    "deadbeef",
    "zog_c"
  };

  long int result;
  long int check;

  size_t numtests = sizeof test / sizeof test[0];

  size_t thistest;

  for(thistest = 0; thistest < numtests; thistest++)
  {
    result = htoi(test[thistest]);
    check = strtol(test[thistest], &endp, 16);

    if((*endp != '\0' && result == -1) || result == check)
    {
      printf("Testing %s. Correct. %ld\n", test[thistest], result);
    }
    else
    {
      printf("Testing %s. Incorrect. %ld\n", test[thistest], result);
    }
  }

  return 0;
}

long htoi (char s[])
{

    char *p = &s[strlen(s)-1];
    long deci = 0, dig = 0;
    int pos = 0;

    while (p >= s) {

        if ((dig = hchartoi(*p, pos)) < 0 ) {
            printf("Error\n");
            return -1;

        }
        deci += dig;
        --p;
        ++pos;

    }
    return deci;
}

/* convert hex char to decimal value */
long hchartoi (char hexdig, int pos)
{

    char hexdigits[] = "0123456789ABCDEF";
    char *p = &hexdigits[0];
    long deci = 0;
    int i;

    while (*p != toupper(hexdig) && deci < 16) {

        ++p;
        ++deci;

    }
    if (*p == toupper(hexdig)) {
       for (i = 0; i < pos; i++)
           deci *= 16;
       return deci;

    }
    return -1;
}


Pilcrow's

/*************************************************
Simple one.
-- Pilcrow
*************************************************/
#include <stdio.h>
#define HEX_LO(N) ((N) >= 'a' && (N) <= 'f')
#define HEX_HI(N) ((N) >= 'A' && (N) <= 'F')
#define HEX_NU(N) ((N) >= '0' && (N) <= '9')
#define IS_HEX(N) (HEX_LO(N) || HEX_HI(N) || HEX_NU(N))

unsigned long htoi(char []);
int main(void)
{
    int i;

    char *test[11] = {
        "F00",
        "bar",
        "0100",
        "0x1",
        "0XA",
        "0X0C0BE",
        "abcdef",
        "123456",
        "0x123456",
        "deadbeef",
        "zog_c"
    };

    for(i =0; i < 11; ++i) {
        printf("%10s %10lu\n",test[i], htoi(test[i]));
    }
    return 0;
}

unsigned long htoi(char hexstr[])
{
    int i;
    long num;
    num = 0;
    i = 0;
    char j;
    if(hexstr[0] == '0' && (hexstr[1] == 'x' || hexstr[1] =='X')) i = 2;

    while(hexstr[i] != '\0') {
        j = hexstr[i];
        if(! IS_HEX(j)) return 0;
        if(HEX_LO(j)) num = num * 16 + 10 + j - 'a';
        else if(HEX_HI(j)) num = num * 16 + 10 + j - 'A';
        else num = num * 16 + j - '0';
        i++;
    }
    return num;
}
Personal tools
Personal tools
Tidy_icons
not logged in