Python 3でのintからバイトへの変換


177

私はこのバイトオブジェクトを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バイトのフォーマットも参照してください)。

http://bugs.python.org/issue3982

これは、bytes(int)がゼロを返すような奇妙さとの相互作用をさらに弱めます

そして:

bytes(int)がそのintのASCII化を返すと、私にとってはるかに便利になります。しかし正直なところ、エラーでもこの動作よりも優れています。(もし私がこの振る舞いが欲しいなら-私は決して持っていません-私はむしろそれが "bytes.zeroes(n)"のように呼び出されるクラスメソッドであることを望みます。)

誰かがこの動作がどこから来ているのか私に説明できますか?


1
タイトルに関連する:3 .to_bytes
jfs

2
整数値3が必要か、それとも3番を表すASCII文字の値(整数値51)が必要かは、質問からはわかりません。最初は、bytes([3])== b '\ x03'です。後者はbytes([ord( '3')])== b'3 'です。
florisla 2017

回答:


177

これが設計された方法です。通常、bytes単一の整数の代わりにイテラブルを呼び出すため、これは理にかなっています。

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

ドキュメントは、この状態だけでなく、ドキュメンテーション文字列のためにbytes

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

25
唯一のpython 2でのpython 3と上記の作品があることに注意してくださいbytesのためだけの別名であるstr手段、bytes([3])あなたを与えるが'[3]'
ボッチャニアク

8
Python 3では、bytes([n])0〜255のint n でのみ機能することに注意してくださいValueError。それ以外の場合は、が発生します。
Acumenus 2016

8
@ABB:バイトのみを0〜255の値を格納することができるので、本当に驚くべきことではない
ティムPietzcker

7
またbytes([3])、OPが求めていたものとはまだ異なることに注意してください。つまり、ASCIIで数字「3」をエンコードするために使用されるバイト値です。bytes([51])つまり、ではb'3'ありませんb'\x03'
レンツ2017

2
bytes(500) len == 500のバイト文字列を作成します。整数500をエンコードするバイト文字列は作成しません。 bytes([500])ます。これが、間違った答えでもある理由です。おそらく正しい答えはint.to_bytes()バージョン3.1以上の場合です。
weberc2

199

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

これはpython 2とpython 3の両方で同じように機能します。

注:逆演算(バイトから整数)は、unpackで実行できます


2
構造体に関係なく、入力の標準的な大きさを有しているので、明確にするため@AndyHayden、 、IH及びBまで作業2**k - 1kはそれぞれ32、16、及び8です。より大きな入力の場合、それらは発生しstruct.errorます。
Acumenus 2016

質問への回答が得られなかったため、おそらく反対票が投じられました:OPはを生成する方法を知りたがっていますb'3\r\n'。つまり、ASCII文字「\ x03」ではなく、ASCII文字「3」を含むバイト文字列
Dave Jones

1
@DaveJones OPが求めているのは何だと思いますか。受け入れ答えが戻っ\x03て、あなただけしたい場合解決策はb'3'簡単です。ABBによって引用された理由ははるかにもっともらしいです...または少なくとも理解できます。
アンディヘイデン

@DaveJonesまた、私がこの回答を追加した理由は、Googleが正確にこれを行うために検索すると、Googleがここに連れて行くためです。それがここにある理由です。
アンディヘイデン

4
これは2と3で同じように機能するだけでなく、Python 3.5のbytes([x])および(x).to_bytes()メソッドよりも高速です。それは予想外でした。
Mark Ransom 2017年

25

Python 3.5以降ではprintf、バイトに%-interpolation(-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」(10進数の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 2014年

1
@ Juanlu001、また"{}\r\n".format(n).encode()デフォルトのutf8エンコーディングを使用しても害はないと思います
John La Rooy

6

3のASCII化はそうではあり"\x33"ません"\x03"

それはpythonが行うstr(3)ことですが、バイナリデータの配列と見なされ、文字列として乱用されないようにする必要があるため、バイトでは完全に間違っています。

あなたが望むものを達成する最も簡単な方法はですbytes((3,))。これはbytes([3])、リストの初期化がはるかに高価であるため、タプルを使用できるときはリストを使用しないためよりも優れています。を使用して、より大きな整数を変換できint.to_bytes(3, "little")ます。

特定の長さのバイトを初期化することは理にかなっており、特定のサイズのメモリを割り当てる必要のあるタイプのバッファを作成するためによく使用されるため、最も有用です。配列を初期化したり、ゼロを書き込んでファイルを拡張したりするときに、これをよく使用します。


1
この回答にはいくつかの問題があります:(a)のエスケープ表記b'3'b'\x33'、ではなくb'\x32'です。(b)(3)はタプルではありません–コンマを追加する必要があります。(c)ゼロでシーケンスを初期化するシナリオはbytes、不変であるため、オブジェクトには適用されません(bytearrayただし、s では意味があります)。
レンツ2017

コメントありがとうございます。私はこれら2つの明らかな間違いを修正しました。以下の場合bytesbytearray、私はそれがほとんど一貫性の問題だと思います。ただし、ゼロをバッファまたはファイルにプッシュする場合にも役立ちます。その場合、データソースとしてのみ使用されます。
バクサウ2017

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 2017

@bjmcはstr.formatに置き換えられました。これはPython 2.6以降で動作するはずです。
renskiy 2017

ありがとう、@ renskiy。全てのPython 3のリリースが見るに、それはのように思えるので、あなたの代わりに「進」の「hex_codec」を使用することをお勧めします「進」の別名は使用できませんstackoverflow.com/a/12917604/845210を
bjmc

@bjmcが修正されました。ありがとう
renskiy 2017

これはpython 3.6の負の整数では失敗します
バーサーカー

4

範囲内の1つのintに対するさまざまなメソッドのパフォーマンスに興味があった[0, 255]ので、タイミングテストを行うことにしました。

下記のタイミングに基づいて、私は多くの異なった価値観や設定をしようとしてから観察し、一般的な傾向から、struct.pack続く、最速のようですint.to_bytesbytesと、と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

テストモジュール(名前は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最初の文で述べたように、私はこれを範囲内の1つのintについてのみ測定しています[0, 255]。「間違った指標」とは、私の測定値がほとんどの状況に適合するほど一般的ではなかったことを意味していると思いますか?それとも私の測定方法は悪かったのですか?後者の場合、私はあなたが言わなければならないことを聞いて興味がありますが、前者の場合、私の測定値がすべてのユースケースに一般的であると主張したことはありません。私の(おそらくニッチな)状況では、範囲内のintのみを処理します。[0, 255]それが、この回答で対処するつもりだったオーディエンスです。私の答えは不明瞭でしたか?わかりやすくするために編集できます...
グラハム

1
範囲の事前計算されたエンコーディングにインデックスを付けるだけのテクニックはどうですか?事前計算はタイミングの影響を受けず、インデックス作成のみが対象になります。
Acumenus

@ABBそれは良い考えです。それは何よりも速くなるようです。少し時間をとって、時間があればこの回答に追加します。
グラハム

3
反復可能なバイト数の時間を本当に計測したい場合は、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)

エンコーダの場合(i + ((i * signed) < 0)).bit_length()i.bit_length()、後者が-128、-32768などの非効率的なエンコーディングにつながるため、代わりにが使用されます。


クレジット:マイナーな非効率性を修正してくれたCervEd。


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

長さ2を使用していない場合は、符号付き整数のビット長を計算し、7を追加してから、符号付き整数の場合は1を追加します。最後に、それをバイト単位の長さに変換します。これにより-128-32768などに対して予期しない結果が生じます
CervEd


これが修正方法です(i+(signed*i<0)).bit_length()
CervEd

3

この動作は、バージョン3より前のPythonでは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の回答とどう違うのですか?
アーサータッカ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.