views:

1601

answers:

6

I'm wanting to read hex numbers from a text file into an unsigned integer so that I can execute Machine instructions. It's just a simulation type thing that looks inside the text file and according to the values and its corresponding instruction outputs the new values in the registers.

For example, the instructions would be:

  • 1RXY -> Save register R with value in memory address XY
  • 2RXY -> Save register R with value XY
  • BRXY -> Jump to register R if xy is this and that etc..
  • ARXY -> AND register R with value at memory address XY

The text file contains something like this each in a new line. (in hexidecimal)

  • 120F
  • B007
  • 290B

My problem is copying each individual instruction into an unsigned integer...how do I do this?

#include <stdio.h>
int main(){
    FILE *f;
    unsigned int num[80];

    f=fopen("values.txt","r");
    if (f==NULL){
     printf("file doesnt exist?!");
    }

    int i=0;
    while (fscanf(f,"%x",num[i]) != EOF){
     fscanf(f,"%x",num[i]);
     i++;
    }
    fclose(f);
    printf("%x",num[0]);
}
A: 

Here's one way:

http://devpinoy.org/blogs/cvega/archive/2006/06/19/xtoi-hex-to-integer-c-function.aspx

John at CashCommons
Why try to write a custom function with limited use when the standard library contains a more generally useful function that's probably far more optimized for your platform?
Chris Lutz
+2  A: 

In this case, all of your input is upper case hex while you are trying to read lower case hex.

To fix it, change %x to %X.

LiraNuna
%i will only read hex if it has a 0x prefix on it...
Chris Dodd
Or you could use `strtol()` which has arbitrary control over what base you want, and might be marginally faster since it doesn't have to parse a format string. Not that I have a natural inclination to shy away from `scanf()` and `printf()` just because of the slight overhead required to parse the format string or anything...
Chris Lutz
Chris: You're right, my bad! I forgot why I thought so... edited.
LiraNuna
This is just wrong - the `%x` and `%X` specifiers are *exactly the same*.
caf
+2  A: 

You're reading two numbers into each element of your array (so you lose half of them as you overwrite them. Try using just

while (i < 80 && fscanf(f,"%x",&num[i]) != EOF)
    i++;

for your loop

edit

you're also missing the '&' to get the address of the array element, so you're passing a random garbage pointer to scanf and probably crashing. The -Wall option is your friend.

Chris Dodd
A: 

Do you want each of the lines (each 4 characters long) separated in 4 different array elements? If you do, I'd try this:

/* read the line */
/* fgets(buf, ...) */

/* check it's correct, mind the '\n' */
/* ... strlen ... isxdigit ... */

/* put each separate input digit in a separate array member */
num[i++] = convert_xdigit_to_int(buf[j++]);

Where the function convert_xdigit_to_int() simply converts '0' (the character) to 0 (an int), '1' to 1, '2' to 2, ... '9' to 9, 'a' or 'A' to 10, ...

Of course that pseudo-code is inside a loop that executes until the file runs out or the array gets filled. Maybe putting the fgets() as the condition for a while(...)

while(/*there is space in the array && */ fgets(...)) {
}
pmg
+3  A: 

You're on the right track. Here's the problems I saw:

  • You need to exit if fopen() return NULL - you're printing an error message but then continuing.
  • Your loop should terminate if i >= 80, so you don't read more integers than you have space for.
  • You need to pass the address of num[i], not the value, to fscanf.
  • You're calling fscanf() twice in the loop, which means you're throwing away half of your values without storing them.

Here's what it looks like with those issues fixed:

#include <stdio.h>

int main() {
    FILE *f;
    unsigned int num[80];
    int i=0;
    int rv;
    int num_values;

    f=fopen("values.txt","r");
    if (f==NULL){
        printf("file doesnt exist?!\n");
        return 1;
    }

    while (i < 80) {
        rv = fscanf(f, "%x", &num[i]);

        if (rv != 1)
            break;

        i++;
    }
    fclose(f);
    num_values = i;

    if (i >= 80)
    {
        printf("Warning: Stopped reading input due to input too long.\n");
    }
    else if (rv != EOF)
    {
        printf("Warning: Stopped reading input due to bad value.\n");
    }
    else
    {
        printf("Reached end of input.\n");
    }

    printf("Successfully read %d values:\n", num_values);
    for (i = 0; i < num_values; i++)
    {
        printf("\t%x\n", num[i]);
    }

    return 0
}
caf
+1 I would have declared `rv` as local to the loop you use it in, but that isn't really a big deal.
Chris Lutz
Having `rv` available outside of the loop to allows you to distinguish between a matching failure and a read failure / EOF.
caf
+3  A: 

You can also use the function strtol(). If you use a base of 16 it will convert your hex string value to an int/long.

errno = 0;
my_int = strtol(my_str, NULL, 16);
/* check errno */

Edit: One other note, various static analysis tools may flag things like atoi() and scanf() as unsafe. atoi is obsolete due to the fact that it does not check for errors like strtol() does. scanf() on the other hand can do a buffer overflow of sorts since its not checking the type sent into scanf(). For instance you could give a pointer to a short to scanf where the read value is actually a long....and boom.

KFro
Don't see why you need to set `errno` to zero...
Chris Lutz
It's because if `strtol` succeeds, it won't change `errno`, and you need to check for this case to distinguish an `ERANGE` error from a succesful conversion that resulted in `LONG_MAX` or `LONG_MIN`.
caf
It's good practice to set errno to zero right before a function call if you're planning to check it immediately after the call. The value of errno may have changed by some other means prior to that call.
John at CashCommons
From the strtol man page: errno = 0; /* To distinguish success/failure after call */ val = strtol(str, /* Check for various possible errors */
KFro