0

How can I convert integer value to ASCII characters in C language? I want to assign characters to array of chars.

char buff[10];

Let's say we have:

int = 93  (HEX: 5D) -> result should be - buff = {']'} 

int = 13398 (HEX: 3456) -> result should be buff = {'4', 'V'}

Similar as is done here

I don't need to care about non printable characters. There will be always printable characters.

Joozty
  • 460
  • 2
  • 12
  • 40
  • 5D (in Hex) is ']'.... Not sure what you want to do with the trailing 'C'...You might want to be more clear how you intend to handle multiple char values in an int – Grantly Dec 29 '17 at 16:58
  • When transforming `5DC` to `']'`. Where is the `C` gone. – alk Dec 29 '17 at 16:58
  • @alk It should be done same way as [here](https://www.rapidtables.com/convert/number/hex-to-ascii.html) So it takes always 8 bites and if there is less as 8 bites left it returns nothing. – Joozty Dec 29 '17 at 17:07
  • What about 0x10000? – alk Dec 29 '17 at 17:16
  • How do you know that 1500 should be `{']'}` and not `{'\x05', '\xDC'}`? – Daniel H Dec 29 '17 at 17:18
  • A good reference is [ASCII Table.com](http://www.asciitable.com/) – David C. Rankin Dec 29 '17 at 17:19
  • @alk buff = {'DLE', 'NUL'} -> DLE - data link escape character and NUL - null character But for the simplying let's say there will be always printable characters. – Joozty Dec 29 '17 at 17:21
  • Ah, so 0x100000 and 0x10000 are the same? Same as 0x1000000? – alk Dec 29 '17 at 17:22
  • @Joozty are you sure "*So it takes always 8 bites and if there is less as 8 bites left it returns nothing*", or is it actually returning the associated non-printable character? – David C. Rankin Dec 29 '17 at 17:23
  • @alk Yes, thank you. Probably there are non printable characters, but it doesn't matter. There will be always printable characters. – Joozty Dec 29 '17 at 17:24
  • @DanielH: The reading seems to go: convert to hex, take the hex digits as characters an parse from the left. – alk Dec 29 '17 at 17:25
  • ... only take pairs, and only the 1st two pairs, else ignore the rest. – alk Dec 29 '17 at 17:26
  • @alk Converting the bytes to ASCII, only to then immediately convert them *back*, seems needlessly complicated (and computationally expensive) to my mind. Just shift out the bytes that you want. – Charles Srstka Dec 29 '17 at 17:41
  • @CharlesSrstka: "convert" was meant to be taken as "to take as" .... see my answer. – alk Dec 29 '17 at 17:44

3 Answers3

4

Just use bit-shifting to get the individual bytes.

Assuming an architecture on which the size of int is 4:

int someInt = ...

uint8_t first = (someInt >> 24);
uint8_t second = (someInt >> 16);
uint8_t third = (someInt >> 8);
uint8_t fourth = someInt;

Now you can just put the resulting bytes into your array. Make sure to check first, second and third to make sure they're not 0 first, and skip them if they are. Make sure to end your array with a null terminator, as required by C strings.

This answer assumes big-endian ordering, since that's what you indicated in your example. If you want little-endian, just reverse the order of the bytes when you put them in the array.

Note that this will turn 5DC into 05 and DC. If you want 5D instead, you should check to see whether the first digit in the original int is 0. You can do this using the & operator, testing the int against 0xf0000000, 0x00f00000, etc. If you find the first digit to be 0, shift the int to the right by 4 bits before extracting the bytes from it.

So, something like this:

void ExtractBytes(int anInt, uint8_t *buf, size_t bufSize) {
    // passing an empty buffer to this function would be stupid,
    // but hey, doesn't hurt to be idiot-proof
    if (bufSize == 0) { return; }

    // Get our sizes
    const int intSize = sizeof(anInt);
    const int digitCount = intSize * 2;

    // find first non-zero digit
    int firstNonZero = -1;
    for (int i = 0; i < digitCount; i++) {
        if ((anInt & (0xf << ((digitCount - 1 - i) * 4))) != 0) {
            firstNonZero = i;
            break;
        }
    }

    if (firstNonZero < 0) {
        // empty string; just bail out.
        buf[0] = 0;
        return;
    }

    // check whether first non-zero digit is even or odd;
    // shift if it's odd
    int intToUse = (firstNonZero % 2 != 0) ? (anInt >> 4) : anInt;

    // now, just extract our bytes to the buffer
    int bufPtr = 0;
    for (int i = intSize - 1; i >= 0; i--) {
        // shift over the appropriate amount, mask against 0xff
        uint8_t byte = (intToUse >> (i * 8));

        // If the byte is 0, we can just skip it
        if (byte == 0) {
            continue;
        }

        // always check to make sure we don't overflow our buffer.
        // if we're on the last byte, make it a null terminator and bail.
        if (bufPtr == bufSize - 1) {
            buf[bufPtr] = 0;
            return;
        }

        // Copy our byte into the buffer
        buf[bufPtr++] = byte;
    }

    // Now, just terminate our string.
    // We can be sure that bufPtr will be less than bufSize,
    // since we checked for that in the loop. So:
    buf[bufPtr] = 0;

    // Aaaaaand we're done
}

Now let's take it for a spin:

uint8_t buf[10];

ExtractBytes(0x41424344, buf, 10);
printf("%s\n", buf);

ExtractBytes(0x4142434, buf, 10);
printf("%s\n", buf);

and the output:

ABCD
ABC
Charles Srstka
  • 16,665
  • 3
  • 34
  • 60
  • Thank you. This seems to be right solution but I would like to do it automatically... Is there any built in function? – Joozty Dec 29 '17 at 17:29
  • 1
    @Joozty There are a few tricks that will *seem* to work, but when you actually try them, they won't. For example, you could use `memcpy` with a pointer to your `int` and copy that into your string, but if you're on a little-endian architecture (and you probably are), that'll turn `0x12345678` into `{ 0x78, 0x56, 0x34, 0x12 }`. You could use the appropriate function for your platform's API to swap the `int` to big-endian first, but then you're still not going to get that last digit removed like you want. Honestly, it's easiest just to do it yourself, IMO. – Charles Srstka Dec 29 '17 at 17:34
  • 1
    @Joozty I fleshed it out for you a bit; check the edit. – Charles Srstka Dec 29 '17 at 18:22
  • Curious, why no `& 0xff` in `uint8_t first = (someInt >> 24);` like the next 3 lines of code? If `someInt == -1` then `someInt >> 24` would still have 24 upper bits to ignore. IAC, It is unnecessary for any of the 4. – chux - Reinstate Monica Dec 29 '17 at 18:55
  • @chux You're right; since they're being stored in a `uint8_t`, that'll all be chopped off anyway. I've been doing a lot of Swift lately, where trying to store something larger than what the type can hold causes a runtime error rather than just chopping it off, and it's a bit in my head at the moment (and the first one would have needed to be masked too anyway; that's my bad. I was thinking of what I typically do with unsigned integers). I've edited the answer; thanks. – Charles Srstka Dec 29 '17 at 19:04
  • @chux (and in case you're curious why I've done a lot of this in the past, the old Mac OS back in the 90s used to store its type codes, creator codes, and resource types in a type called `OSType`, which was `typedef`ed to a 32-bit unsigned integer. The tools to set these all involved a text field that you could type characters (in the Mac OS Roman charset) into, so these were almost always made up of printable characters. So, if you were presenting one to the user, you'd be writing that function to do it. Mac users, after all, expected the type of an application to be 'APPL', not 0x4150504c.) – Charles Srstka Dec 29 '17 at 19:23
1

convert integer value to ASCII characters in C language?...

Referring to an ASCII table, the value of ']' in C will always be interpreted as 0x5D, or decimal value 93. While the value of "]" in C will always be interpreted as a NULL terminated char array, i.e., a string representation comprised of the values:

|93|\0|  

(As illustrated in This Answer, similar interpretations are valid for all ASCII characters.)

To convert any of the integer (char) values to something that looks like a "]", you can use a string function to convert the char value to a string representation. For example all of these variations will perform that conversion:

char strChar[2] = {0};

sprintf(strChar, "%c", ']'); 
sprintf(strChar, "%c", 0x5D); 
sprintf(strChar, "%c", 93);  

and each produce the identical C string: "]".

I want to assign characters to array of chars...

example of how to create an array of char, terminated with a NULL char, such as "ABC...Z":

int i;
char strArray[27] = {0};
for(i=0;i<26;i++)
{
     strArray[i] = i+'A';
}
strArray[i] = 0;
printf("Null terminated array of char: %s\n", strArray);
ryyker
  • 22,849
  • 3
  • 43
  • 87
0
unsigned u = ...;

if (0x10 > u)
  exit(EXIT_FAILURE);

while (0x10000 < u) u /= 2;
while (0x1000 > u) u *= 2;

char c[2] = {u / 0x100, u % 0x100);
alk
  • 69,737
  • 10
  • 105
  • 255
  • What about bigger numbers DEC(1212696648), HEX(48484848) - ASCII(FFFF)? – Joozty Dec 29 '17 at 17:37
  • @Joozty: Answering to my questions you commented to treat 0x10000 the same as 0x100000 as 0x1000000 .... – alk Dec 29 '17 at 17:42