Welcome, guest | Sign In | My Account | Store | Cart

I write a lot of ad-hoc protocol analysers using Python. Generally, I'm dealing with a byte stream that I want to output as a string of hex. Sometimes, I want to convert it back again. Eventually, I got round to putting the functions in a module so I wouldn't keep cut and pasting them :)

Python, 67 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
"""
HexByteConversion

Convert a byte string to it's hex representation for output or visa versa.

ByteToHex converts byte string "\xFF\xFE\x00\x01" to the string "FF FE 00 01"
HexToByte converts string "FF FE 00 01" to the byte string "\xFF\xFE\x00\x01"
"""

#-------------------------------------------------------------------------------

def ByteToHex( byteStr ):
    """
    Convert a byte string to it's hex string representation e.g. for output.
    """
    
    # Uses list comprehension which is a fractionally faster implementation than
    # the alternative, more readable, implementation below
    #   
    #    hex = []
    #    for aChar in byteStr:
    #        hex.append( "%02X " % ord( aChar ) )
    #
    #    return ''.join( hex ).strip()        

    return ''.join( [ "%02X " % ord( x ) for x in byteStr ] ).strip()

#-------------------------------------------------------------------------------

def HexToByte( hexStr ):
    """
    Convert a string hex byte values into a byte string. The Hex Byte values may
    or may not be space separated.
    """
    # The list comprehension implementation is fractionally slower in this case    
    #
    #    hexStr = ''.join( hexStr.split(" ") )
    #    return ''.join( ["%c" % chr( int ( hexStr[i:i+2],16 ) ) \
    #                                   for i in range(0, len( hexStr ), 2) ] )
 
    bytes = []

    hexStr = ''.join( hexStr.split(" ") )

    for i in range(0, len(hexStr), 2):
        bytes.append( chr( int (hexStr[i:i+2], 16 ) ) )

    return ''.join( bytes )

#-------------------------------------------------------------------------------

# test data - different formats but equivalent data
__hexStr1  = "FFFFFF5F8121070C0000FFFFFFFF5F8129010B"
__hexStr2  = "FF FF FF 5F 81 21 07 0C 00 00 FF FF FF FF 5F 81 29 01 0B"
__byteStr = "\xFF\xFF\xFF\x5F\x81\x21\x07\x0C\x00\x00\xFF\xFF\xFF\xFF\x5F\x81\x29\x01\x0B"


if __name__ == "__main__":
    print "\nHex To Byte and Byte To Hex Conversion"

    print "Test 1 - ByteToHex - Passed: ", ByteToHex( __byteStr ) == __hexStr2
    print "Test 2 - HexToByte - Passed: ", HexToByte( __hexStr1 ) == __byteStr
    print "Test 3 - HexToByte - Passed: ", HexToByte( __hexStr2 ) == __byteStr

    # turn a non-space separated hex string into a space separated hex string!
    print "Test 4 - Combined  - Passed: ", \
          ByteToHex( HexToByte( __hexStr1 ) ) == __hexStr2

I don't want to start a 'one line bubble sort' kind of competition here but, if anyone can come up with a line comprehension implementation for the HexToByte function that is faster than the alternative, that would be interesting.

6 comments

Andrew Henshaw 14 years, 8 months ago  # | flag

remove strip. You can avoid the use of the strip call in the line:

return ''.join( [ "%02X " % ord( x ) for x in byteStr ] ).strip()

by joining with a space:

return ' '.join( [ "%02X" % ord( x ) for x in byteStr ] )
Simon Peverett (author) 14 years, 8 months ago  # | flag

remove strip means remove space separation. If you remove the space separation then you don't need the .strip(), this is true But then test 4 kind of sucks.

Also, I like my hex byte output space separated. It makes it easier to read.

Eduardo Bustamante 11 years, 5 months ago  # | flag

What Andrew Henshaw meant is that if you join a list of strings with a space, the space is only added between the strings, so there is no need to call the strip function.

If you have this:

li = ['01', '01', '30']

and you call

' '.join(li) # Note the string is a space, not an empty string.

the result will be

'01 01 30'

not

'01 01 30 '

so there will be no need to call the strip function.

Friedrich Hagedorn 10 years, 2 months ago  # | flag

Here is one alternative solution for the hex2byte competition:

def int2byte(val, width=32):
    """
    Convert a signed integer value of specified width into a byte string.
    """
    if val < 0:
        val = val + (1 << width)
    return ''.join([chr((val >> 8*n) & 255) for n in reversed(range(width/8))])

A hex2byte conversion is done by

>>> int2byte(-0x10)
'\xff\xff\xff\xf0'
Friedrich Hagedorn 10 years, 2 months ago  # | flag

And here is my version of byte2int:

def byte2int(bstr, width=32):
    """
    Convert a byte string into a signed integer value of specified width.
    """
    val = sum(ord(b) << 8*n for (n, b) in enumerate(reversed(bstr)))
    if val >= (1 << (width - 1)):
        val = val - (1 << width)
    return val

A byte2hex conversion is done by

>>> hex(byte2int('\xff\xff\xff\xf0'))
'-0x10'

And here is a verification of the two functions:

>>> byte2int(int2byte(-16))
-16

But I dont know if these versions are faster than the original ones.

James Thomas Moon 9 years, 1 month ago  # | flag

You can also use python module binascii and function hexlify ( http://docs.python.org/library/binascii.html#binascii.hexlify )

import uuid , binascii
u = binascii.hexlify(uuid.uuid4().bytes)
print("UUID %s" % u)

It will print something like

UUID 68d9677cf198449e8b5acce7b8ef280b
Created by Simon Peverett on Wed, 21 Mar 2007 (PSF)
Python recipes (4591)
Simon Peverett's recipes (3)

Required Modules

  • (none specified)

Other Information and Tasks