在Python 3中将int转换为字节

时间:2014-01-09 10:31:49

标签: python python-3.x

我试图在Python 3中构建这个字节对象:

b'3\r\n'

所以我尝试了显而易见的(对我而言),并发现了一种奇怪的行为:

>>> bytes(3) + b'\r\n'
b'\x00\x00\x00\r\n'

显然:

>>> bytes(10)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

我一直无法看到有关字节转换为什么以这种方式阅读文档的指针。但是,我确实在Python问题中发现了一些关于将format添加到字节的惊喜消息(另请参阅Python 3 bytes formatting):

http://bugs.python.org/issue3982

  

这与奇怪的事情(如字节(int)现在返回零)相互作用更加糟糕

  

如果bytes(int)返回该int的ASCIIfication,对我来说会更方便;但老实说,即使错误也会比这种行为更好。 (如果我想要这种行为 - 我从来没有 - 我宁愿它是一个类方法,调用类似“bytes.zeroes(n)”。)

有人可以解释一下这种行为的来源吗?

16 个答案:

答案 0 :(得分:131)

从python 3.2你可以做到

>>> (1024).to_bytes(2, byteorder='big')
b'\x04\x00'

https://docs.python.org/3/library/stdtypes.html#int.to_bytes

def int_to_bytes(x):
    return x.to_bytes((x.bit_length() + 7) // 8, 'big')

def int_from_bytes(xbytes):
    return int.from_bytes(xbytes, 'big')

因此,x == int_from_bytes(int_to_bytes(x))

答案 1 :(得分:120)

这就是它的设计方式 - 它有意义,因为通常,你会在一个可迭代而不是一个整数上调用bytes

>>> bytes([3])
b'\x03'

docs state this以及bytes的文档字符串:

 >>> help(bytes)
 ...
 bytes(int) -> bytes object of size given by the parameter initialized with null bytes

答案 2 :(得分:34)

您可以使用struct's pack

In [11]: struct.pack(">I", 1)
Out[11]: '\x00\x00\x00\x01'

">"是byte-order (big-endian)和"我"是format character。因此,如果您想要做其他事情,您可以具体:

In [12]: struct.pack("<H", 1)
Out[12]: '\x01\x00'

In [13]: struct.pack("B", 1)
Out[13]: '\x01'

这在python 2和python 3上都是一样的。

注意:可以使用unpack完成逆操作(字节到int)。

答案 3 :(得分:19)

Python 3.5+ introduces %-interpolation (printf-style formatting) for bytes

>>> b'%d\r\n' % 3
b'3\r\n'

请参阅PEP 0461 -- Adding % formatting to bytes and bytearray

在早期版本中,您可以使用str.encode('ascii')结果:

>>> s = '%d\r\n' % 3
>>> s.encode('ascii')
b'3\r\n'

注意:它与what int.to_bytes produces不同:

>>> n = 3
>>> n.to_bytes((n.bit_length() + 7) // 8, 'big') or b'\0'
b'\x03'
>>> b'3' == b'\x33' != '\x03'
True

答案 4 :(得分:10)

文档说:

bytes(int) -> bytes object of size given by the parameter
              initialized with null bytes

序列:

b'3\r\n'

字符'3'(十进制51)字符'\ r'(13)和'\ n'(10)。

因此,方式会对其进行处理,例如:

>>> bytes([51, 13, 10])
b'3\r\n'

>>> bytes('3', 'utf8') + b'\r\n'
b'3\r\n'

>>> n = 3
>>> bytes(str(n), 'ascii') + b'\r\n'
b'3\r\n'

在IPython 1.1.0&amp; Python 3.2.3

答案 5 :(得分:5)

3的ASCII化是"\x33"而非"\x03"

这就是python对str(3)的作用,但对于字节来说这是完全错误的,因为它们应该被视为二进制数据的数组而不是被滥用为字符串。

实现所需内容的最简单方法是bytes((3,)),这比bytes([3])更好,因为初始化列表要贵得多,所以在使用元组时永远不要使用列表。您可以使用int.to_bytes(3, "little")转换更大的整数。

使用给定长度初始化字节是有意义的并且是最有用的,因为它们通常用于创建某种类型的缓冲区,您需要为其分配给定大小的内存。我经常在初始化数组或通过向它写零来扩展某些文件时使用它。

答案 6 :(得分:5)

int(包括Python2的long)可以使用以下函数转换为bytes

import codecs

def int2bytes(i):
    hex_value = '{0:x}'.format(i)
    # make length of hex_value a multiple of two
    hex_value = '0' * (len(hex_value) % 2) + hex_value
    return codecs.decode(hex_value, 'hex_codec')

逆向转换可以由另一个完成:

import codecs
import six  # should be installed via 'pip install six'

long = six.integer_types[-1]

def bytes2int(b):
    return long(codecs.encode(b, 'hex_codec'), 16)

这两个函数都适用于Python2和Python3。

答案 7 :(得分:3)

来自bytes docs

  

因此,构造函数参数被解释为bytearray()。

然后,从bytearray docs

  

可选的source参数可用于以几种不同的方式初始化数组:

     
      
  • 如果是整数,则数组将具有该大小,并将使用空字节进行初始化。
  •   

请注意,这与2.x(其中x> = 6)行为不同,其中bytes只是str

>>> bytes is str
True

PEP 3112

  

2.6 str以各种方式与3.0的字节类型不同;最值得注意的是,构造函数完全不同。

答案 8 :(得分:3)

这种行为来自这样一个事实,即在版本3之前的Python中bytes只是str的别名。在Python3.x bytesbytearray的不可变版本 - 全新类型,不向后兼容。

答案 9 :(得分:3)

我对[0, 255]范围内单个整数的各种方法的性能感到好奇,所以我决定进行一些计时测试。

根据下面的时间安排,从我尝试许多不同的值和配置观察到的总体趋势来看,struct.pack似乎是最快的,其次是int.to_bytesbytesstr.encode(毫不奇怪)是最慢的。请注意,结果显示的变化远大于所表示的变化,int.to_bytesbytes有时会在测试过程中切换速度排名,但是struct.pack显然是最快的。

在Windows上的CPython 3.7中得到的结果:

Testing with 63:
bytes_: 100000 loops, best of 5: 3.3 usec per loop
to_bytes: 100000 loops, best of 5: 2.72 usec per loop
struct_pack: 100000 loops, best of 5: 2.32 usec per loop
chr_encode: 50000 loops, best of 5: 3.66 usec per loop

测试模块(名为int_to_byte.py):

"""Functions for converting a single int to a bytes object with that int's value."""

import random
import shlex
import struct
import timeit

def bytes_(i):
    """From Tim Pietzcker's answer:
    https://stackoverflow.com/a/21017834/8117067
    """
    return bytes([i])

def to_bytes(i):
    """From brunsgaard's answer:
    https://stackoverflow.com/a/30375198/8117067
    """
    return i.to_bytes(1, byteorder='big')

def struct_pack(i):
    """From Andy Hayden's answer:
    https://stackoverflow.com/a/26920966/8117067
    """
    return struct.pack('B', i)

# Originally, jfs's answer was considered for testing,
# but the result is not identical to the other methods
# https://stackoverflow.com/a/31761722/8117067

def chr_encode(i):
    """Another method, from Quuxplusone's answer here:
    https://codereview.stackexchange.com/a/210789/140921

    Similar to g10guang's answer:
    https://stackoverflow.com/a/51558790/8117067
    """
    return chr(i).encode('latin1')

converters = [bytes_, to_bytes, struct_pack, chr_encode]

def one_byte_equality_test():
    """Test that results are identical for ints in the range [0, 255]."""
    for i in range(256):
        results = [c(i) for c in converters]
        # Test that all results are equal
        start = results[0]
        if any(start != b for b in results):
            raise ValueError(results)

def timing_tests(value=None):
    """Test each of the functions with a random int."""
    if value is None:
        # random.randint takes more time than int to byte conversion
        # so it can't be a part of the timeit call
        value = random.randint(0, 255)
    print(f'Testing with {value}:')
    for c in converters:
        print(f'{c.__name__}: ', end='')
        # Uses technique borrowed from https://stackoverflow.com/q/19062202/8117067
        timeit.main(args=shlex.split(
            f"-s 'from int_to_byte import {c.__name__}; value = {value}' " +
            f"'{c.__name__}(value)'"
        ))

答案 10 :(得分:1)

尽管先前的answer by brunsgaard是一种有效的编码,但它仅适用于无符号整数。此代码基于它来处理有符号和无符号整数。

def int_to_bytes(i: int, *, signed: bool = False) -> bytes:
    length = (i.bit_length() + 7 + int(signed)) // 8
    return i.to_bytes(length, byteorder='big', signed=signed)

def bytes_to_int(b: bytes, *, signed: bool = False) -> int:
    return int.from_bytes(b, byteorder='big', signed=signed)

# Test unsigned:
for i in range(1025):
    assert i == bytes_to_int(int_to_bytes(i))

# Test signed:
for i in range(-1024, 1025):
    assert i == bytes_to_int(int_to_bytes(i, signed=True), signed=True)

答案 11 :(得分:1)

所有其他方法不适用于大量数字。

将整数转换为十六进制表示,然后将其转换为字节:

def int_to_bytes(number):
    hrepr = hex(number).replace('0x', '')
    if len(hrepr) % 2 == 1:
        hrepr = '0' + hrepr
    return bytes.fromhex(hrepr)

结果:

>>> int_to_bytes(2**256 - 1)
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'

答案 12 :(得分:0)

如果要处理二进制表示,最好使用 ctypes

import ctypes
x = ctypes.c_int(1234)
bytes(x)

您必须使用特定的整数表示(有符号/无符号和位数:c_uint8c_int8c_unit16、...)。

答案 13 :(得分:0)

我认为您可以先将 int 转换为 str,然后再转换为 byte。 这应该会产生你想要的格式。

bytes(str(your_number),'UTF-8') + b'\r\n'

它在 py3.8 中对我有用。

答案 14 :(得分:-1)

如果您不关心性能,可以先将int转换为str。

number = 1024
str(number).encode()

答案 15 :(得分:-2)

如果问题是如何将整数本身(而不是等效的字符串)转换为字节,我认为可靠的答案是:

>>> i = 5
>>> i.to_bytes(2, 'big')
b'\x00\x05'
>>> int.from_bytes(i.to_bytes(2, 'big'), byteorder='big')
5

有关这些方法的更多信息,请点击此处:

  1. https://docs.python.org/3.8/library/stdtypes.html#int.to_bytes
  2. https://docs.python.org/3.8/library/stdtypes.html#int.from_bytes