tags:

views:

555

answers:

5

I'm trying to write a program that takes input of - hexadecimals, octals, and decimals -, stores them in integer variables, and outputs them along with their conversion to decimal form. For example:

User inputs: 0x43, 0123, 65

Program outputs:

0x43 hexadecimal converts to 67 decimal
0123 octal converts to 83 decimal
65 decimal converts to 65 decimal

So obviously I need a way to interpret the numbers, but I'm not sure how to go about doing it. I've tried various methods such as reading them into a function and converting them into a string, and vice versa (see here for code examples), but interpreting the numbers always requires conversion to some format that trashes the original input.

The only thing I can think of is overloading a >> operator that reads a character at a time and if it sees 0x or 0 at the beginning of the input then it stores the whole input into a string before it is read into an int. Then the program would somehow have to determine the right manipulator during output.

Not sure if there is a simpler way to do this, any help is appreciated.

Edit: This has been solved, but I decided to post the code in if anyone is interested.

#include "std_lib_facilities.h"

void number_sys(string num, string& s)
{
  if(num[0] == '0' && (num[1] != 'x' && num[1] != 'X')) s = "octal";
  else if(num[0] == '0' && (num[1] == 'x' || num[1] == 'X')) s = "hexadecimal";
  else s = "decimal";
}

int main()
{
    cout << "Input numbers in hex, dec, or oct. Use 0xx to cancel.\n";
    string a;

    while(cin >> a){
    if(a == "0xx")break;
    string atype;
    number_sys(a, atype);

    int anum = strtol(a.c_str(), NULL, 0);

    cout << a << setw(20-a.length()) << atype << setw(20) << "converts to" << setw(10)
         << anum << setw(10) << "decimal\n";
                 }

    keep_window_open();
}
+10  A: 

Take a look at the strtol function.

char * args[3] = {"0x43", "0123", "65"};
for (int i = 0; i < 3; ++i) {
  long int value = strtol(args[i], NULL, 0);
  printf("%s converts to %d decimal\n", args[i], value);
}

Outputs:

0x43 converts to 67 decimal
0123 converts to 83 decimal
65 converts to 65 decimal
Dave DeLong
@Welbog *shrug* it's something that can be easily found by googling. I had to find this exact function a couple of weeks ago for a homework assignment.
Dave DeLong
This isn't homework. If you read my profile I'm self learning from a book.
trikker
Oh. Well, you should remove the homework tag. That really skews how a lot of us answer these things.
Welbog
I didn't put it there.
trikker
That doesn't mean you can't get rid of it.
Welbog
Having a hard time implementing this since I haven't learned about pointers yet. Plus I'm using string variables rather than inputting the actual strings in the char * args[3]
trikker
This worked, implemented it a bit differently but it worked! Thanks! (Using c_str(), null, 0, as arguments)
trikker
I could do this in my head
Janie
@Janie that's cool. It's an exercise for programming, I could write down "Hello World" on a piece of paper much easier than I could program it as well (or could I :P)
trikker
A: 

If you literally have to use a single integer variable for storage of all the information that you need in order to display your final output, then you have to use part of the integer variable for storing the original base that the input was in. Otherwise it's not recoverable.

chaos
A: 

You could always store it as a string to start, and look at the first two characters to see if they are 0x:

std::string num;
std::cin >> num;

if (num[0] == '0' && num[1] == 'x')
{ 
  //handle
}
Hooked
C standard library functions (like strtol) are a better approach.
Quinn Taylor
@Quinn: Since strtol doesn't tell you what base the number was in, and the OP wants to print "octal", "hexadecimal", "decimal" in the output, some manual base-determination of this kind is necessary even if you're using strtol to do the conversion. So "handle" should mean 'print "hexadecimal" and then call strtol'.
Steve Jessop
@onebyone, the printing function for the base type has already been handled. I just needed help with the conversion.
trikker
@hooked, that still doesn't deal with the conversion from string to int, it just reads the string which I've already done.
trikker
+1  A: 

I'm not sure if there is a C++ way of doing this, but if you don't mind a little C-ishness, you can read the thing into a char array and use something like sscanf(buffer, "%i", &output). The %i interprets the input as hex, octal or decimal depending on its format, just like you describe.

Edit: Ah, didn't know that strtol could also do this. Ignore me.

Thomas
C standard library functions (like strtol) are a better approach.
Quinn Taylor
I agree that `strtol` is better than `sscanf`, but `sscanf` is also a C standard library function. The format specifier `%i` is quite standard too.
Thomas
+1  A: 

If you want to preserve the base information (hex/oct/dec), you will need to store that information separately from the integer value itself, and it will require you to parse at least the first couple of characters of the input string (sscanf(), strtol(), etc., won't preserve that information for you).

You could roll your own mini-parser that saves the input base and does the conversion (code off the top of my head, untested):

char inputStr[MAX_INPUT_LENGTH+1];
char *p;
int result = 0;
char values[128];

/**
 * This enumeration serves double duty; it keeps track of what
 * base the input was entered in, and it controls the state machine 
 * used to parse the input; from a didactic POV, this is probably bad form
 */
enum {
  eStart, 
  eHexOrOctal, 
  eOctal, 
  eDecimal, 
  eHexadecimal, 
  eError
} eBase = eStart;


/**
 * Use the values array as a table to map character constants to their corresponding 
 * integer values.  This is safer than using an expression like *p - '0', in 
 * that it can work with character encodings where digits are not consecutive.
 * Yes, this wastes a little space, but the convenience makes
 * up for it IMO.  There are probably better ways to do this.
 */
values['0'] = 0; values['1'] = 1; values['2'] = 2; values['3'] = 3; 
values['4'] = 4; values['5'] = 5; values['6'] = 6; values['7'] = 7; 
values['8'] = 8; values['9'] = 9; values['a'] = 10; values['b'] = 11; 
values['c'] = 12; values['d'] = 13; values['e'] = 14; values['f'] = 15;

/**  
 * Insert code to get input string here 
 */

for (p = inputStr; *p != 0; p++)
{
  /**
   * Cycle through each character in the input string, adjusting the state
   * of the parser as necessary.  Parser starts in the eStart state.
   */
  switch(eBase)
  {
    /**
     * Start state -- we haven't parsed any characters yet
     */
    case eStart:
      if (*p == '0') eBase = eHexOrOctal; // leading 0 means either hex or octal
      else if (isdigit(*p))
      {
        eBase = eDecimal;    // leading non-0 digit means decimal
        result = values[*p];  
      }                    
      else eBase = eError;    // no other character may start an integer constant
      break;
    /**
     * HexOrOctal -- we've read a leading 0, which could start either a hex or
     * octal constant; we need to read the second character to make a determination
     */
    case eHexOrOctal:      
      if (tolower(*p) == 'x')  base = eHexadecimal;
      else if (isdigit(*p) && *p != '8' && *p != '9')
      {
        base = eOctal;   
        result = values[*p];
      }
      else eBase = eError;
      break;
    /**
     * Octal -- we are parsing an octal constant
     */
    case eOctal:
      if (isdigit(*p) && *p != '8' && *p != '9')
      {
        result *= 8;
        result += values[*p];
      }
      else eBase = eError;
      break;
    /**
     * Decimal -- we are parsing a decimal constant
     */
    case eDecimal:
      if (isdigit(*p))
      {
        result *= 10;
        result += values[*p];
      }
      else eBase = eError;
      break;
    /**
     * Hexadecimal -- we are parsing a hex constant
     */
    case eHexadecimal:
      if (isxdigit(*p))
      {
        result *= 16;
        result += values[tolower(*p)];
      }
      else eBase = eError;
      break;
    /**
     * String is not a properly formatted integer constant in 
     * any base; once we fall into the error state, we stay there.
     */
    case eError:
    default:
      break;
  }
}
if (eBase != eError)
{
  printf("input: %s ", inputStr); fflush(stdout);
  switch(eBase)
  {
    case eOctal: printf("octal "); break;
    case eHexadecimal: printf("hexadecimal "); break
    default: break;
  }
  fflush(stdout);
  printf("converts to %d decimal\n", result);
}
else
{
  /** Print a suitable error message here */
}
John Bode