为什么“bytes(n)”会创建一个长度为 n 字节的字符串,而不是将 n 转换为二进制表示形式?

2025-02-07 08:44:00
admin
原创
67
摘要:问题描述:我试图在 Python 3 中构建这个字节对象:`b'3'`所以我尝试了显而易见的方法(对我来说),并发现了一种奇怪的行为:>>> bytes(3) + b' ' b'x00x00x00 ' 显然:>>> bytes(10) b'x00x00x00x00x00x0...

问题描述:

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

`b'3
'`

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

>>> bytes(3) + b'
'
b'x00x00x00
'

显然:

>>> bytes(10)
b'x00x00x00x00x00x00x00x00x00x00'

在阅读文档时,我找不到任何关于字节转换为何以这种方式工作的提示。不过,我确实在这个 Python 问题中发现了一些关于添加format到字节的意外消息(另请参阅Python 3 字节格式):

http://bugs.python.org/issue3982

这与 bytes(int) 现在返回零之类的奇怪现象的交互甚至更差

和:

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

有人能向我解释一下这种行为从何而来吗?


解决方案 1:

从 Python 3.2 开始你可以使用to_bytes

>>> (1024).to_bytes(2, byteorder='big')
b'x04x00'
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')

因此,x == int_from_bytes(int_to_bytes(x))。请注意,上述编码仅适用于无符号(非负)整数。

对于有符号整数,位长度的计算稍微有点棘手:

def int_to_bytes(number: int) -> bytes:
    return number.to_bytes(length=(8 + (number + (number < 0)).bit_length()) // 8, byteorder='big', signed=True)

def int_from_bytes(binary_data: bytes) -> Optional[int]:
    return int.from_bytes(binary_data, byteorder='big', signed=True)

解决方案 2:

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

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

文档说明了这一点,以及的文档字符串bytes

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

解决方案 3:

您可以使用结构的包:

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

“>” 是字节顺序 (大端字节序),“I” 是格式字符。因此,如果您想做其他事情,可以具体说明:

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

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

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

注意:可以使用unpack进行逆操作(将字节转换为整数) 。

解决方案 4:

Python 3.5+printf为字节引入了 % 插值( -style 格式):

>>> b'%d
' % 3
b'3
'

参见PEP 0461——向字节和字节数组添加 % 格式。

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

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

注意:它与生成int.to_bytes的不同:

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

解决方案 5:

文档说:

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

顺序如下:

b'3
'

它是字符‘3’(十进制 51)、字符‘\r’(13)和‘\n’(10)。

因此,方法将这样处理,例如:

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

>>> bytes('3', 'utf8') + b'
'
b'3
'

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

在 IPython 1.1.0 和 Python 3.2.3 上测试

解决方案 6:

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

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

实现所需目的的最简单方法是bytes((3,)),这比 更好,bytes([3])因为初始化列表的成本要高得多,因此当可以使用元组时,切勿使用列表。您可以使用 转换更大的整数int.to_bytes(3, "little")

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

解决方案 7:

尽管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)

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


感谢 CervEd 修复了一个小效率低下的问题。

解决方案 8:

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

解决方案 9:

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。

解决方案 10:

由于您想要处理二进制表示,所以最好使用ctypes

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

您必须使用特定的整数表示形式(有符号/无符号和位数:c_uint8,,,...)。c_int8`c_unit16`

解决方案 11:

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

根据下面的时间安排,以及我尝试许多不同值和配置后观察到的总体趋势,struct.pack似乎是最快的,其次是int.to_bytesbytes和 ,str.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)'"
        ))

解决方案 12:

来自字节文档:

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

然后,来自bytearray 文档:

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

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

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

>>> bytes is str
True

PEP 3112:

2.6 的 str 与 3.0 的 bytes 类型在很多方面都不同;最明显的是,构造函数完全不同。

解决方案 13:

有些答案不适用于大数字。

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

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'xffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxffxff'

解决方案 14:

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

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

它在py3.8中对我有用。

解决方案 15:

>>> chr(116).encode()
b't'
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用