ose
ose

Reputation: 4075

bytearray.fromhex doesn't convert when no letters in number

I am trying to convert a number (of arbitrary size, possibly very long) to its corresponding string of bytes. For example, inputting the number 1094795585 (base 10), which is 0x41414141 (base 16), should return "\x41\x41\x41\x41".

Currently I have:

def number_to_bytes(number):
    hex_string = hex(number).rstrip("L").lstrip("0x")
    return bytearray.fromhex(hex_string.decode("hex"))

When I input the number 1094795585 (0x41414141), I get the error "odd-length string".

When I input the number 1094795584 (0x41414140), I get the error "non-hexadecimal number found in fromhex() arg at position 2".

This makes me think that Python is adding some sort of invisible character in the hex_string. Is this the case?

How can I achieve the correct conversion?

Upvotes: 4

Views: 9326

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1122382

You should not decode the string from hex; drop the .decode('hex') call. You need to pass in actual hex digits, not a bytestring with codepoints based on those digits.

Compare the difference:

>>> hex(1094795584)[2:]
'41414140'
>>> hex(1094795584)[2:].decode('hex')
'AAA@'

By decoding you are already producing the very bytes you wanted bytesarray.fromhex() to produce; you could just use bytesarray(hex_string.decode('hex')) in that case.

You can use format() to produce a hex format for your number that doesn't include the 0x prefix, nor a L postfix for long integers:

import math

def number_to_bytes(number):
    byte_count = int(math.log(number, 256)) + 1
    hex_string = '{:0{}x}'.format(number, byte_count * 2)
    return bytearray.fromhex(hex_string)

Demo:

>>> import math
>>> def number_to_bytes(number):
...     nibble_count = int(math.log(number, 256)) + 1
...     hex_string = '{:0{}x}'.format(number, nibble_count * 2)
...     return bytearray.fromhex(hex_string)
...
>>> number_to_bytes(1094795585)
bytearray(b'AAAA')
>>> number_to_bytes(1094795584)
bytearray(b'AAA@')

Upvotes: 2

Amir
Amir

Reputation: 6186

This should work:

import math    
def number_to_bytes(num):
    hex_str = format(num, 'x')
    hex_len = (int(math.log2(num)/8)+1)*2
    return bytearray.fromhex(hex_str.zfill(hex_len))

Upvotes: 0

Related Questions