source

왜 "bytes(n)"는 n을 이진 표현으로 변환하는 대신 길이 n 바이트 문자열을 생성합니까?

manycodes 2023. 5. 6. 15:13
반응형

왜 "bytes(n)"는 n을 이진 표현으로 변환하는 대신 길이 n 바이트 문자열을 생성합니까?

파이썬 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'

설명서를 읽으면서 바이트 변환이 왜 이런 식으로 작동하는지에 대한 포인터를 볼 수 없었습니다.하지만 이번 파이썬 이슈에서 추가에 대한 몇 가지 놀라운 메시지를 발견했습니다.format바이트 수(Python 3바이트 형식 참조):

http://bugs.python.org/issue3982

이는 바이트(int)가 현재 0을 반환하는 것과 같은 이상한 것과 훨씬 더 나쁘게 상호 작용합니다.

그리고:

바이트(int)가 해당 int의 ASCII화를 반환한다면 훨씬 편리하겠지만, 솔직히 이 동작보다 오류가 더 나을 것입니다.(이 동작을 원했다면 - 저는 결코 그런 적이 없었습니다 - 저는 차라리 "바이트"처럼 호출되는 클래스 메소드가 되고 싶습니다.0(n)".

누가 이 행동이 어디서 왔는지 설명해 줄 수 있나요?

python 3.2에서 다음을 사용할 수 있습니다.

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

그것은 그렇게 설계되었습니다 - 그리고 그것은 말이 됩니다 왜냐하면 보통, 당신이 전화를 하기 때문입니다.bytes단일 정수 대신 반복 가능:

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

문서에는 다음에 대한 문서 문자열과 함께 다음과 같이 명시되어 있습니다.bytes:

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

구조체의 팩을 사용할 수 있습니다.

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

">"는 바이트 순서(빅 엔디언)이고 "I"는 형식 문자입니다.따라서 다른 작업을 수행하려는 경우 구체적으로 설명할 수 있습니다.

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

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

이것은 python 2와 python 3 모두에서 동일하게 작동합니다.

참고: 역방향 작업(바이트에서 int)은 언팩으로 수행할 수 있습니다.

Python 3.5+는 바이트에 대해 %-간삽(-printfstyle 형식)을 도입했습니다.

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

PEP 0461 - 바이트바이트 배열에 % 형식 추가를 참조하십시오.

이전 버전에서는 다음을 사용할 수 있습니다.str그리고..encode('ascii')결과:

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

참고: 이 기능은 다음을 생성하는 기능과 다릅니다.

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

설명서에는 다음과 같이 나와 있습니다.

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 및 Python 3.2.3에서 테스트됨

의 ASCII 은 3의 ASCII ."\x33"것은 아니다."\x03"!

그것이 파이썬이 하는 일입니다.str(3)그러나 바이트는 이진 데이터 배열로 간주되어야 하고 문자열로 악용되어서는 안 되므로 바이트에 대해서는 완전히 잘못된 것입니다.

당신이 원하는 것을 성취하는 가장 쉬운 방법은bytes((3,))어느 쪽이 더 나은 것입니다.bytes([3])목록을 초기화하는 것이 훨씬 더 비싸기 때문에 튜플을 사용할 수 있을 때는 목록을 사용하지 마십시오.다을사용더여하큰변수있다습니환할정을 하여 더 큰 할 수 .int.to_bytes(3, "little").

지정된 길이의 바이트를 초기화하는 것이 가장 유용하며, 이러한 바이트는 지정된 크기의 메모리가 필요한 버퍼 유형을 만드는 데 자주 사용되기 때문입니다.배열을 초기화하거나 파일에 0을 기록하여 파일을 확장할 때 자주 사용합니다.

범위 내 단일 int에 대한 다양한 방법의 성능이 궁금했습니다.[0, 255]그래서 타이밍 테스트를 해보기로 했습니다.

을 바탕으로, 아의타이과다밍양값구과시한로바성관탕으추일세를인래,struct.pack가장 빠른 것 같고, 그 뒤를 따릅니다.int.to_bytes,bytes 와께함과 함께str.encode(놀랍게도) 가장 느립니다.결과는 표시된 것보다 더 많은 변동을 보여줍니다.int.to_bytes그리고.bytes, 테트중에때속때순전도만환지했위를로스,▁ranking만▁during▁sometimes지전▁speed▁but▁switched했,struct.pack분명히 가장 빠릅니다.

윈도우즈에서 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

(이름은 "Test module)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)'"
        ))

3 의 Python에서 Python에서 3이라는 의 Python에서 했습니다.bytes에 대한 가명일 뿐입니다.str 3 Python 3.bytes는 의불버전다니의 입니다.bytearray완전히 새로운 유형으로, 이전 버전과 호환되지 않습니다.

바이트 문서에서:

따라서 생성자 인수는 bytearray()로 해석됩니다.

그런 다음 bytearray 문서에서:

옵션인 source 매개 변수는 다음과 같은 몇 가지 방법으로 배열을 초기화하는 데 사용할 수 있습니다.

  • 정수인 경우 배열의 크기는 해당 크기이며 null 바이트로 초기화됩니다.

(서 x > =6. 여기서 참고, 이는 2.x (x > = 6) 동작다.bytes간단히 말하면str:

>>> bytes is str
True

PEP 3112:

2.6 str은 3.0의 바이트 유형과 다양한 점에서 다릅니다. 가장 중요한 점은 생성자가 완전히 다르다는 것입니다.

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 모두에서 작동합니다.

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.

는 표현을처면다려음사이진것좋가다습니장이용는하을리하▁represent▁as다▁best좋▁the▁to니습▁is▁useation,▁with▁binary가이▁you장을 사용하는 것이 가장 좋습니다.ctypes.

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

표현 및 : 특서정부정/표호및수 비트현수없음명서부수::▁you▁represent▁the)을 .c_uint8,c_int8,c_unit16,...).

일부 대답은 큰 숫자와 함께 사용할 수 없습니다.

정수를 16진수 표현으로 변환한 다음 바이트로 변환합니다.

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'

바이트로 변환하기 전에 int를 str로 변환할 수 있다고 생각합니다.그것은 당신이 원하는 형식을 만들어 낼 것입니다.

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

py3.8에서 작동합니다.

만약 문제가 정수 자체를 (동일한 문자열이 아닌) 바이트로 변환하는 방법이라면, 강력한 대답은 다음과 같습니다.

>>> 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
>>> chr(116).encode()
b't'

언급URL : https://stackoverflow.com/questions/21017698/why-does-bytesn-create-a-length-n-byte-string-instead-of-converting-n-to-a-b

반응형