在Python 3中将int转换为字节

ast*_*nlu 147 python python-3.x

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

b'3\r\n'

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

>>> bytes(3) + b'\r\n'
b'\x00\x00\x00\r\n'
Run Code Online (Sandbox Code Playgroud)

显然:

>>> bytes(10)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Run Code Online (Sandbox Code Playgroud)

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

http://bugs.python.org/issue3982

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

和:

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

有人可以解释一下这种行为来自哪里?

bru*_*ard 162

从python 3.2你可以做到

>>> (1024).to_bytes(2, byteorder='big')
b'\x04\x00'
Run Code Online (Sandbox Code Playgroud)

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

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

def int_from_bytes(xbytes: bytes) -> int:
    return int.from_bytes(xbytes, 'big')
Run Code Online (Sandbox Code Playgroud)

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

  • 正如问题所问,这对于从 `3` 获取 `b"3"` 没有帮助。(它会给出 `b"\x03"`。) (4认同)
  • 虽然这个答案很好,但仅适用于无符号(非负)整数。我已经改写了一个[answer](/sf/answers/3789898801/),它也适用于有符号整数。 (2认同)

Tim*_*ker 141

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

>>> bytes([3])
b'\x03'
Run Code Online (Sandbox Code Playgroud)

文档说明这一点,以及文档字符串为bytes:

 >>> help(bytes)
 ...
 bytes(int) -> bytes object of size given by the parameter initialized with null bytes
Run Code Online (Sandbox Code Playgroud)

  • 请注意以上只适用于python 3.在python 2中``bytes`只是`str`的​​别名,这意味着`bytes([3])`给你`'[3]'`. (21认同)
  • 还应该注意,`bytes([3])`仍然与OP想要的不同 - 即用于编码ASCII中的数字"3"的字节值,即.`bytes([51])`,这是'b'3',而不是'b'\ x03'. (7认同)
  • 在Python 3中,请注意`bytes([n])`仅适用于从0到255的int n。对于其他任何情况,它都会引发`ValueError`。 (5认同)
  • @A-B-B: Not really surprising since a byte can only store values between 0 and 255. (5认同)
  • `bytes(500)` 创建了一个带有 len == 500 的字节串。它不会创建一个对整数 500 进行编码的字节串。我同意 `bytes([500])` 不能工作,这就是为什么答案也错了。对于 >= 3.1 的版本,正确的答案可能是 `int.to_bytes()`。 (2认同)

And*_*den 37

你可以使用struct的包:

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

">"是字节顺序(big-endian),"I"是格式字符.因此,如果您想要做其他事情,您可以具体:

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

In [13]: struct.pack("B", 1)
Out[13]: '\x01'
Run Code Online (Sandbox Code Playgroud)

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

注意:逆操作(字节到int)可以通过解压缩来完成.

  • 它不仅在2和3中的工作方式相同,而且比Python 3.5中的`bytes([x])`和`(x).to_bytes()`方法更快.那是出乎意料的. (3认同)
  • @AndyHayden为了澄清,由于结构具有标准尺寸而不管输入如何,"I","H"和"B"一直工作到"2**k - 1",其中k分别为32,16和8.对于较大的输入,它们会引发`struct.error`. (2认同)
  • @DaveJones 是什么让您认为这是 OP 想要的?**accepted answer** 返回 `\x03`,如果你只想要 `b'3'` 的解决方案是微不足道的。ABB 引用的原因更合理……或者至少可以理解。 (2认同)

jfs*_*jfs 21

Python 3.5+ printf为字节引入了%-interpolation(样式格式):

>>> b'%d\r\n' % 3
b'3\r\n'
Run Code Online (Sandbox Code Playgroud)

请参阅PEP 0461 - 将%格式添加到字节和bytearray.

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

>>> s = '%d\r\n' % 3
>>> s.encode('ascii')
b'3\r\n'
Run Code Online (Sandbox Code Playgroud)

注:这是从不同的东西int.to_bytes产生:

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


小智 10

文件说:

bytes(int) -> bytes object of size given by the parameter
              initialized with null bytes
Run Code Online (Sandbox Code Playgroud)

序列:

b'3\r\n'
Run Code Online (Sandbox Code Playgroud)

字符'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'
Run Code Online (Sandbox Code Playgroud)

在IPython 1.1.0和Python 3.2.3上测试过


Acu*_*nus 6

尽管brunsgaard 之前的答案是一种有效的编码,但它仅适用于无符号整数。这个是在它的基础上构建的,适用于有符号和无符号整数。

def int_to_bytes(i: int, *, signed: bool = False) -> bytes:
    length = ((i + ((i * signed) < 0)).bit_length() + 7 + 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)
Run Code Online (Sandbox Code Playgroud)

对于编码器,(i + ((i * signed) < 0)).bit_length()使用 ,而不是仅仅i.bit_length()因为后者会导致 -128、-32768 等低效编码。


信用:CervEd 修复了一个小的效率低下的问题。


Bac*_*sau 5

3的ASCII化"\x33"不是"\x03"!

这就是python所做的,str(3)但是字节完全错误,因为它们应该被认为是二进制数据的数组而不是被滥用为字符串.

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

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


ren*_*kiy 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')
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

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


Gra*_*ham 5

我对 range 中单个 int 的各种方法的性能很好奇[0, 255],所以我决定做一些计时测试。

根据下面的时间安排,以及我从尝试许多不同的值和配置中观察到的总体趋势,struct.pack似乎是最快的,其次是int.to_bytes, bytes,并且str.encode(不出所料)是最慢的。请注意,结果显示的变化比所表示的要多,int.to_bytes并且bytes有时会在测试期间切换速度排名,但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
Run Code Online (Sandbox Code Playgroud)

测试模块(命名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:
    /sf/answers/1471248411/
    """
    return bytes([i])

def to_bytes(i):
    """From brunsgaard's answer:
    /sf/answers/2126263891/
    """
    return i.to_bytes(1, byteorder='big')

def struct_pack(i):
    """From Andy Hayden's answer:
    /sf/answers/1884467651/
    """
    return struct.pack('B', i)

# Originally, jfs's answer was considered for testing,
# but the result is not identical to the other methods
# /sf/answers/2223320571/

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

    Similar to g10guang's answer:
    /sf/answers/3609115331/
    """
    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 /sf/ask/1334354171/
        timeit.main(args=shlex.split(
            f"-s 'from int_to_byte import {c.__name__}; value = {value}' " +
            f"'{c.__name__}(value)'"
        ))
Run Code Online (Sandbox Code Playgroud)

  • 如果你真的想对可迭代的字节进行计时,你应该使用 `bytes((i,))` 而不是 `bytes([i])`,因为列表更复杂,使用更多的内存并且需要很长时间来初始化. 在这种情况下,一事无成。 (3认同)