Python 3에서 int를 바이트로 변환


177

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

이것은 bytes (int)와 같은 홀수와 더 잘 상호 작용하지 않습니다.

과:

bytes (int)가 해당 int의 ASCIIfication을 반환하면 훨씬 편리합니다. 그러나 솔직히 말해서이 행동보다 오류가 더 낫습니다. (내가 가진 적이없는이 행동을 원한다면 오히려 "bytes.zeroes (n)"과 같은 클래스 메쏘드가되고 싶다.

이 행동의 출처를 누군가가 설명 할 수 있습니까?


1
제목 관련 :3 .to_bytes
jfs

2
정수 값 3 또는 숫자 3을 나타내는 ASCII 문자 값 (정수 값 51)을 원하는지 확실하지 않습니다. 첫 번째는 bytes ([3]) == b '\ x03'입니다. 후자는 bytes ([ord ( '3')]) == b'3 '입니다.
florisla

답변:


177

이것이 설계된 방식입니다. 일반적으로 bytes단일 정수 대신 iterable을 호출하기 때문에 의미가 있습니다 .

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

문서는 다음에 대한 문서 문자열뿐만 아니라 이것을 나타 냅니다 bytes.

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

25
단지 파이썬 2에서 파이썬 3으로 위의 작품 것을주의 bytes단지 별칭 str수단 bytes([3])을 제공가 '[3]'.
botchniaque

8
파이썬 3에서는 bytes([n])int n에서만 0에서 255까지만 작동합니다 ValueError.
Acumenus

8
@ABB : 바이트가 0에서 255 사이의 값만 저장할 수 있으므로 놀랍지 않습니다.
Tim Pietzcker

7
또한 bytes([3])OP가 원했던 것과는 다른 점 , 즉 ASCII로 숫자 "3"을 인코딩하는 데 사용되는 바이트 값, 즉 여전히 다릅니다. bytes([51]), b'3'그렇지 않습니다 b'\x03'.
lenz

2
bytes(500)len == 500으로 바이트 문자열을 만듭니다. 정수 500을 인코딩하는 바이트 문자열을 만들지 않습니다. 그리고 bytes([500])작동하지 않는다는 데 동의합니다. 이것이 잘못된 대답이기도합니다. 아마도 정답은 int.to_bytes()>> 3.1 버전입니다.
weberc2 2016 년

199

파이썬 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: 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)). 이 인코딩은 부호없는 (음이 아닌) 정수에 대해서만 작동합니다.


4
이 답변은 좋지만 부호없는 (음수가 아닌) 정수에만 작동합니다. 나는 부호있는 정수에서도 작동 하는 을 쓰도록 적응시켰다 .
Acumenus

1
즉 점점에 도움이되지 않습니다 b"3"에서 3질문 요청으로. (그것은 줄 것이다 b"\x03".)
gsnedders

40

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

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'

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

참고 : unpack을 사용 하여 역 연산 (바이트에서 int)을 수행 할 수 있습니다 .


2
구조체에 관계없이 입력의 표준 크기를 갖기 때문에, 명확히하기 @AndyHayden, I, HB까지 작업 2**k - 1k는 각각 32, 16, 8이다. 더 큰 입력의 경우 증가 struct.error합니다.
Acumenus

아마도 질문에 대한 답을 얻지 못했기 때문에 다운 투표를 한 것으로 보입니다. OP는 생성 방법 b'3\r\n', 즉 ASCII 문자 "\ x03"이 아닌 ASCII 문자 "3"을 포함하는 바이트 문자열 을 알고 싶어합니다.
Dave Jones

1
@DaveJones 이것이 OP가 원하는 것이라고 생각하게 만드는 것은 무엇입니까? 허용 대답의 반환 \x03, 당신은 단지 원하는 경우 해결책은 b'3'간단하다. ABB가 인용 한 이유는 훨씬 더 타당하거나 적어도 이해할 수 있습니다.
Andy Hayden

@DaveJones 또한, 내가이 답변을 추가 한 이유는 Google이 정확하게이 작업을 수행 할 때 여기로 안내하기 때문입니다. 그래서 여기에 있습니다.
Andy Hayden

4
이것은 2와 3에서 동일하게 작동 할뿐만 아니라 Python 3.5 의 bytes([x])(x).to_bytes()메소드 보다 빠릅니다 . 예상치 못한 일이었습니다.
Mark Ransom

25

Python 3.5 이상에서는 printf바이트에 대한 % 보간 ( -style 형식)을 도입했습니다 .

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

참고 : 그것은 생산 하는 것과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

11

설명서는 다음과 같이 말합니다.

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에서 테스트


1
나는 일을 결국 bytes(str(n), 'ascii') + b'\r\n'str(n).encode('ascii') + b'\r\n'. 감사! :)
astrojuanlu

1
@ Juanlu001, 또한 "{}\r\n".format(n).encode()기본 utf8 인코딩을 사용하여 아무런 해가 없다고 생각하지 않습니다
John La Rooy

6

3의 ASCIIfication은 "\x33"아닙니다 "\x03"!

그것은 파이썬이하는 일 str(3)이지만 바이너리 데이터의 배열로 간주되고 문자열로 남용되지 않아야하기 때문에 바이트에는 완전히 잘못되었습니다.

원하는 것을 얻는 가장 쉬운 방법 bytes((3,))은입니다 bytes([3]).리스트를 초기화하는 것이 훨씬 비싸기 때문에 튜플을 사용할 수있을 때리스트를 사용하지 마십시오. 을 사용하여 더 큰 정수를 변환 할 수 있습니다 int.to_bytes(3, "little").

주어진 길이로 바이트를 초기화하는 것은 의미가 있으며 가장 유용합니다. 종종 주어진 크기의 메모리가 필요한 일부 버퍼 유형을 만드는 데 사용되기 때문입니다. 배열을 초기화하거나 0을 써서 파일을 확장 할 때 종종 이것을 사용합니다.


1
(가) 이스케이프 표기 :이 답변에 몇 가지 문제가 있습니다 b'3'IS b'\x33',하지가 b'\x32'. (b) (3)튜플이 아니므로 쉼표를 추가해야합니다. (c) 0으로 시퀀스를 초기화하는 시나리오는 bytes객체가 불변이기 때문에 적용되지 않습니다 ( bytearray그렇지만 의미가 있습니다 ).
lenz

귀하의 의견에 감사드립니다. 나는 그 두 가지 명백한 실수를 고쳤다. bytes및의 경우 bytearray주로 일관성 문제라고 생각합니다. 그러나 0을 버퍼 나 파일로 푸시하려는 경우에도 유용합니다.이 경우 데이터 소스로만 사용됩니다.
Bachsau

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


'hex_value ='% x '% i'는 Python 3.4에서 작동하지 않습니다. TypeError가 발생하므로 대신 hex ()를 사용해야합니다.
bjmc

@bjmc는 str.format으로 대체되었습니다. 이것은 Python 2.6 이상에서 작동합니다.
renskiy

감사합니다, @renskiy. 'hex'대신 'hex_codec'를 사용하고 싶을 수 있습니다. 'hex'별칭은 모든 Python 3 릴리스에서 사용할 수없는 것 같습니다. stackoverflow.com/a/12917604/845210
bjmc

@bjmc 고정. 감사합니다
renskiy

이것은 파이썬 3.6의 음의 정수에서 실패합니다
Berserker

4

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

아래의 타이밍을 바탕으로, 나는 많은 다른 가치와 구성을 시도하는 관찰 일반적인 경향에서, struct.pack다음, 가장 빠른 것 같다 int.to_bytes, bytes및과을 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)'"
        ))

1
@ABB 첫 번째 문장에서 언급했듯이 범위의 단일 int에 대해서만 측정하고 있습니다 [0, 255]. "잘못된 표시기"라고 가정하면 내 측정 값이 대부분의 상황에 맞지 않을 정도로 일반적이지 않다는 것을 의미합니까? 또는 측정 방법이 열악합니까? 후자의 경우, 나는 당신이해야 할 말을 듣고 싶지만, 전자의 경우, 모든 측정 사례에 대해 내 측정이 일반적이라고 주장하지 않았습니다. 내 (아마도 틈새) 상황의 경우 범위의 int 만 다루고 있으며이 [0, 255]답변을 해결하려는 독자입니다. 대답이 명확하지 않습니까? 명확성을 위해 편집 할 수 있습니다 ...
Graham

1
범위에 대해 미리 계산 된 인코딩을 인덱싱하는 기술은 어떻습니까? 사전 계산에는 타이밍이 적용되지 않으며 인덱싱 만 적용됩니다.
Acumenus

@ABB 좋은 생각입니다. 다른 것보다 빠를 것 같습니다. 시간이 있으면 타이밍을 조정 하고이 답변에 추가하겠습니다.
Graham

3
반복 가능한 바이트의 시간을 실제로 계산하려면 list가 더 복잡하고 더 많은 메모리를 사용하고 초기화하는 데 시간이 걸리기 때문에 bytes((i,))대신 대신 사용해야 합니다 bytes([i]). 이 경우 아무것도 없습니다.
Bachsau

4

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)

인코더 의 경우 후자가 -128, -32768 등의 비효율적 인 인코딩을 초래하기 때문에 (i + ((i * signed) < 0)).bit_length()대신에 사용됩니다 i.bit_length().


크레딧 : 경미한 비 효율성 해결을위한 CervEd.


int_to_bytes(-128, signed=True) == (-128).to_bytes(1, byteorder="big", signed=True)False
CervEd

길이 2를 사용하지 않고 부호있는 정수의 비트 길이를 계산하고 부호있는 정수 인 경우 7을 더한 다음 1을 추가합니다. 마지막으로 바이트 단위의 길이로 변환합니다. 이에 대한 예기치 않은 결과 산출 -128, -32768
CervEd


이것은 당신이 그것을 고치는 방법(i+(signed*i<0)).bit_length()
CervEd

3

이 동작은 Python 3 이전 버전의에 bytes대한 별칭 일뿐입니다 str. Python3.x bytes에서 불변 버전은 bytearray완전히 새로운 유형이며 이전 버전과 호환되지 않습니다.


3

에서 문서 바이트 :

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

그런 다음 bytearray docs에서 :

선택적 source 매개 변수를 사용하여 몇 가지 다른 방식으로 배열을 초기화 할 수 있습니다.

  • 정수이면 배열의 크기가 설정되며 null 바이트로 초기화됩니다.

참고 2.X 상이한 행동, 여기서 (x> = 6 임) 것이 bytes간단하다 str:

>>> bytes is str
True

PEP 3112 :

2.6 str은 3.0의 바이트 유형과 여러 가지면에서 다릅니다. 특히, 생성자는 완전히 다릅니다.


0

일부 답변은 큰 숫자로 작동하지 않습니다.

정수를 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'

1
"다른 모든 방법은 많은 수에서 작동하지 않습니다." 사실이 아니며 int.to_bytes모든 정수와 함께 작동합니다.
juanpa.arrivillaga

@ juanpa.arrivillaga 그래, 내 나쁜. 내 답변을 편집했습니다.
Max Malysh

-1

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

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

1
이것은 5 년 전에 게시되었으며 현재 가장 높은 투표 응답 인 brunsgaard의 답변과 어떻게 다른가요?
Arthur Tacca
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.