82

I have a non-negative int and I would like to efficiently convert it to a big-endian string containing the same data. For example, the int 1245427 (which is 0x1300F3) should result in a string of length 3 containing three characters whose byte values are 0x13, 0x00, and 0xf3.

My ints are on the scale of 35 (base-10) digits.

How do I do this?

Serenity
  • 35,289
  • 20
  • 120
  • 115
fish
  • 1,970
  • 1
  • 18
  • 18
  • 1
    Does this answer your question? [Convert string from big-endian to little-endian or vice versa in Python](https://stackoverflow.com/questions/46109815/convert-string-from-big-endian-to-little-endian-or-vice-versa-in-python) – TAbdiukov Dec 03 '19 at 13:36

9 Answers9

71

In Python 3.2+, you can use int.to_bytes:

If you don't want to specify the size

>>> n = 1245427
>>> n.to_bytes((n.bit_length() + 7) // 8, 'big') or b'\0'
b'\x13\x00\xf3'

If you don't mind specifying the size

>>> (1245427).to_bytes(3, byteorder='big')
b'\x13\x00\xf3'
Janus Troelsen
  • 20,267
  • 14
  • 135
  • 196
  • 12
    How to do this in 2.6? – Kimvais Jul 31 '13 at 12:58
  • 4
    If your integer is signed, add `signed=True`. – gerrit Apr 30 '14 at 22:47
  • @gerrit: OP says *"I have a non-negative int "*. It is not enough to pass `signed=True` to support negative integers e.g., try `n=-129`. – jfs Feb 04 '16 at 15:06
  • 2
    Am I right in thinking the `(length + 7) // 8` part is the same as doing `math.ceil(length / 8)` ? If so, I think it'd be clearer to use that option – Jezzamon Apr 20 '16 at 01:27
  • @Jezzamon it is not the same, you could have tested with QuickCheck: ```import pytest; import math; @pytest.mark.randomize(length=int, ncalls=100) def test_generate_ints(length): assert (length + 7) // 8 == math.ceil(length / 8) ``` . even it if were, why use floating point math if you do not need it? – Janus Troelsen Apr 20 '16 at 12:21
  • 1
    @JanusTroelsen They are the same, as long as the `n.bit_length()` is a reasonable size (you need around a quadrillion bits for there to be an issue, which means that `n` is going to be unbelievably large). In my situation readability is much more important than a performance increase, and numbers will never be that big. – Jezzamon Apr 20 '16 at 22:44
  • This bytes string still fails a `.decode('utf-8')` with a UnicodeDecodeError. – Kebman Oct 09 '20 at 14:03
  • 1
    @Kebman why would they be valid UTF-8? I don't see why that would be a case. Maybe ask a new well-written question and ping me, and I'll try to respond. – Janus Troelsen Oct 27 '20 at 18:03
  • And, reverse? bytes to int? – e-info128 Nov 12 '20 at 06:03
  • @e-info128 `int.from_bytes` – Janus Troelsen Nov 12 '20 at 22:42
66

You can use the struct module:

import struct
print(struct.pack('>I', your_int))

'>I' is a format string. > means big endian and I means unsigned int. Check the documentation for more format chars.

phoenix
  • 7,988
  • 6
  • 39
  • 45
Ayman Hourieh
  • 132,184
  • 23
  • 144
  • 116
  • 11
    struct.pack returns a fixed length string and doesn't seem to have facilities for handling large ints. I suppose I could break my int into powers of 2^32, run it through struct.pack(), and reassemble the result, but that seems like a lot of work...do you know of a simpler way? – fish May 10 '09 at 20:33
  • 2
    I couldn't find a library to handle arbitrary long ints. I think you will have to implement it yourself. Other answers contain implementations. – Ayman Hourieh May 10 '09 at 21:00
  • Ayman, note that Python has built-in support for arbitrarily long ints, so you don't need a library. In Python 3, there will only be the `int` type, but even now in Python 2.4+, `int`s are automatically converted to Python `long`s when they overflow 32 bits (signed). – Ben Hoyt May 10 '09 at 21:58
  • 2
    benhoyt, thanks for the comment. I'm aware of this. I was talking about handling the conversion of arbitrary long ints to big endian. Not handling them in general. – Ayman Hourieh May 10 '09 at 22:25
15

This is fast and works for small and (arbitrary) large ints:

def Dump(n): 
  s = '%x' % n
  if len(s) & 1:
    s = '0' + s
  return s.decode('hex')
print repr(Dump(1245427))  #: '\x13\x00\xf3'
pts
  • 80,836
  • 20
  • 110
  • 183
  • 3
    As a variant of the above, one can replace `if len(s) & 1` with `if len(s) % 2` (both are ture if there are an odd amount of hexadecimal characters), and `'%x' % n` with `'{0:x}'.format(n)` (both of which format the number as a hexadecimal string). – Abbafei Feb 23 '15 at 08:22
11

Probably the best way is via the built-in struct module:

>>> import struct
>>> x = 1245427
>>> struct.pack('>BH', x >> 16, x & 0xFFFF)
'\x13\x00\xf3'
>>> struct.pack('>L', x)[1:]  # could do it this way too
'\x13\x00\xf3'

Alternatively -- and I wouldn't usually recommend this, because it's mistake-prone -- you can do it "manually" by shifting and the chr() function:

>>> x = 1245427
>>> chr((x >> 16) & 0xFF) + chr((x >> 8) & 0xFF) + chr(x & 0xFF)
'\x13\x00\xf3'

Out of curiosity, why do you only want three bytes? Usually you'd pack such an integer into a full 32 bits (a C unsigned long), and use struct.pack('>L', 1245427) but skip the [1:] step?

Ben Hoyt
  • 10,694
  • 5
  • 60
  • 84
7

Single-source Python 2/3 compatible version based on @pts' answer:

#!/usr/bin/env python
import binascii

def int2bytes(i):
    hex_string = '%x' % i
    n = len(hex_string)
    return binascii.unhexlify(hex_string.zfill(n + (n & 1)))

print(int2bytes(1245427))
# -> b'\x13\x00\xf3'
Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
7
def tost(i):
  result = []
  while i:
    result.append(chr(i&0xFF))
    i >>= 8
  result.reverse()
  return ''.join(result)
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 3
    tost(0) returns an empty string. Needs an i == 0 test before the while loop if the desired result is '\x00' – Mike Ellis Mar 31 '14 at 14:20
  • It would be better off with a try/except than an if i==0, on the assumption it doesn't happen most of the time. – J.J Mar 08 '17 at 22:12
3

The shortest way, I think, is the following:

import struct
val = 0x11223344
val = struct.unpack("<I", struct.pack(">I", val))[0]
print "%08x" % val

This converts an integer to a byte-swapped integer.

ctuffli
  • 3,559
  • 4
  • 31
  • 43
xwin
  • 355
  • 3
  • 3
  • This only will not use the most efficient number of bytes for some numbers, e.g. 70000 – J.J Mar 08 '17 at 22:07
2

Using the bitstring module:

>>> bitstring.BitArray(uint=1245427, length=24).bytes
'\x13\x00\xf3'

Note though that for this method you need to specify the length in bits of the bitstring you are creating.

Internally this is pretty much the same as Alex's answer, but the module has a lot of extra functionality available if you want to do more with your data.

Scott Griffiths
  • 21,438
  • 8
  • 55
  • 85
-2

Very easy with pwntools , the tools created for software hacking

(Un-ironically, I stumbled across this thread and tried solutions here, until I realised there exists conversion functionality in pwntools)

import pwntools

x2 = p32(x1)
TAbdiukov
  • 1,185
  • 3
  • 12
  • 25