0

I receive a 32-bit number over the serial line, using num = ser.read(4). Checking the value of num in the shell returns something like a very unreadable b'\xcbu,\x0c'.

I can check against the ASCII table to find the values of "u" and ",", and determine that the hex value of the received number is actually equal to "cb 75 2c 0c", or in the format that Python outputs, it's b'\xcb\x75\x2c\x0c'. I can also type the value into a calculator and convert it to decimal (or run int(0xcb752c0c) in Python), which returns 3413453836.

How can I do this conversion from a binary string literal to an integer in Python?

jayded-bee
  • 103
  • 4

2 Answers2

2

I found two alternatives to solve this problem.

  1. Using the int.from_bytes(bytes, byteorder, *, signed=False) method
  2. Using the struct.unpack(format, buffer) from the builtin struct module

Using int.from_bytes

Starting from Python 3.2, you can use int.from_bytes.

Second argument, byteorder, specifies endianness of your bytestring. It can be either 'big' or 'little'. You can also use sys.byteorder to get your host machine's native byteorder.

from the docs:

The byteorder argument determines the byte order used to represent the integer. If byteorder is "big", the most significant byte is at the beginning of the byte array. If byteorder is "little", the most significant byte is at the end of the byte array. To request the native byte order of the host system, use sys.byteorder as the byte order value.

int.from_bytes(bytes, byteorder, *, signed=False)

Code applying in your case:

>>> import sys
>>> int.from_bytes(b'\x11', byteorder=sys.byteorder)
17
>>> bin(int.from_bytes(b'\x11', byteorder=sys.byteorder))
'0b10001'

Here is the official demonstrative code from the docs:

>>> int.from_bytes(b'\x00\x10', byteorder='big')
16
>>> int.from_bytes(b'\x00\x10', byteorder='little')
4096
>>> int.from_bytes(b'\xfc\x00', byteorder='big', signed=True)
-1024
>>> int.from_bytes(b'\xfc\x00', byteorder='big', signed=False)
64512
>>> int.from_bytes([255, 0, 0], byteorder='big')
16711680

Using the struct.unpack method

The function you need to achieve your goal is struct.unpack.

To understand where you can use it, we need to understand the parameters to give and their impact on the result.

struct.unpack(format, buffer)

Unpack from the buffer buffer (presumably packed by pack(format, ...)) according to the format string format. The result is a tuple even if it contains exactly one item. The buffer’s size in bytes must match the size required by the format, as reflected by calcsize().

The buffer is the bytes that we have to give and the format is how the bytestring should be read.

The information will be split into a string, format characters, that can be endianness, ctype, bytesize, ecc..

from the docs:

Format characters have the following meaning; the conversion between C and Python values should be obvious given their types. The ‘Standard size’ column refers to the size of the packed value in bytes when using standard size; that is, when the format string starts with one of '<', '>', '!' or '='. When using native size, the size of the packed value is platform-dependent.

This table represents the format characters currently avaiable in Python 3.10.6:

Format C-Type Standard Size
x pad byte
c char 1
b signed char 1
B uchar 1
? bool 1
h short 2
H ushort 2
i int 4
I uint 4
l long 4
L ulong 4
q long long 8
Q unsigned long long 8
n ssize_t
N unsigned ssize_t
f float
d double
s char[]
p char[]
P void*

and here is a table to use it to correct byte order:

Character Byte order Size Alignment
@ native native Native
= native standard None
< little-endian standard None
> big-endian standard None
! network (= big-endian) standard None

Examples

Here is an example how you can use it:

>>> import struct
>>> format_chars = '<i' #4 bytes, endianness is 'little'
>>> struct.unpack(format_chars,b"f|cs")
(1935899750,)
XxJames07-
  • 1,833
  • 1
  • 4
  • 17
  • 1
    struct supports as many bytes as fit the format, you just have to use the correct type. `h` is for short which is 16 bits. `i` is for int which is 32 bits. https://docs.python.org/3/library/struct.html#format-characters – Blop Jul 10 '22 at 11:25
1

check the builtin struct module. https://docs.python.org/3/library/struct.html

in your case, it should probably be something like:

import struct

struct.unpack(">I", b'\xcb\x75\x2c\x0c')

but it depends on Endianness and signed/unsigned, so do read the entire doc.

Blop
  • 473
  • 2
  • 6