Bir bayt dizesini int'ye nasıl dönüştürebilirim?


162

Bir bayt dizesini python'da int'e nasıl dönüştürebilirim?

Böyle söyle: 'y\xcc\xa6\xbb'

Bunu yapmanın akıllıca / aptalca bir yolunu buldum:

sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))

Yerleşik veya standart kütüphanede bunu daha basit yapan bir şey olması gerektiğini biliyorum ...

Bu, int (xxx, 16) kullanabileceğiniz onaltılık basamak dizesini dönüştürmekten farklıdır , ancak bunun yerine gerçek bayt değerleri dizesini dönüştürmek istiyorum.

GÜNCELLEME:

James'in biraz daha iyi cevap vermesini seviyorum, çünkü başka bir modül almayı gerektirmiyor, ancak Greg'in yöntemi daha hızlı:

>>> from timeit import Timer
>>> Timer('struct.unpack("<L", "y\xcc\xa6\xbb")[0]', 'import struct').timeit()
0.36242198944091797
>>> Timer("int('y\xcc\xa6\xbb'.encode('hex'), 16)").timeit()
1.1432669162750244

Benim hacky yöntemi:

>>> Timer("sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))").timeit()
2.8819329738616943

DAHA FAZLA GÜNCELLEME:

Birisi yorumlarda başka bir modülü içe aktarmayla ilgili sorunun ne olduğunu sordu. Bir modülü içe aktarmak mutlaka ucuz değil, bir göz atın:

>>> Timer("""import struct\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""").timeit()
0.98822188377380371

Modülü içe aktarma maliyetini dahil etmek, bu yöntemin sahip olduğu avantajın neredeyse tamamını ortadan kaldırır. Bunun, tüm kıyaslama çalışması için yalnızca bir kez içe aktarma masrafını içereceğine inanıyorum; her seferinde yeniden yüklemeye zorladığımda ne olduğuna bakın:

>>> Timer("""reload(struct)\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""", 'import struct').timeit()
68.474128007888794

Söylemeye gerek yok, bir içe aktarma başına bu yöntemin çok sayıda yürütme yapıyorsanız, bu oranla daha az sorun olur. Ayrıca, cpu yerine muhtemelen i / o maliyeti olduğundan, belirli bir makinenin kapasitesine ve yük özelliklerine bağlı olabilir.


ve standart lib'dan bir şeyler almak kötü, neden?


26
"daha fazla güncellemeniz" garip ... modülü neden bu kadar sık ​​alıyorsunuz?

5
Bunun eski bir soru olduğunu biliyorum. Ancak karşılaştırmanızı diğer insanlar için güncel tutmak istiyorsanız: Mekanik salyangozun cevabı ( int.from_bytes) struct.unpackbilgisayarımda gerçekleştirildi . Daha okunabilir imo olmanın yanında.
magu_

Yanıtlar:


110

Bunu yapmak için struct modülünü de kullanabilirsiniz :

>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L

3
Uyarı: "L" aslında 64 bit Python derlemelerinde 8 bayttır (4 değil), bu yüzden orada başarısız olabilir.
Rafał Dowgird

12
Rafał: Gerçekten değil, Greg <kullandığından, L belgelerine göre standart boyut (4) "biçim dizgisi '<', '>', '!' veya '='. " docs.python.org/library/struct.html#format-characters
André Laszlo

59
Bu yanıt, rasgele uzunluktaki ikili dizelerde işe yaramaz.
amcnabb

4
Türlerin belirli boyutları vardır, asla keyfi uzunluktaki ikili dizgiler için çalışmaz. Her öğenin türünü biliyorsanız, bunun için bir for döngüsü ayarlayabilirsiniz.
Joshua Olson

2
"L" aslında uint32'dir (4 bayt). Benim durumumda olduğu gibi 8 bayta ihtiyacınız varsa, "Q" -> uint64 kullanın. Ayrıca "l" -> int32 ve q -> int64
ntg

319

Python 3.2 ve sonraki sürümlerde

>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='big')
2043455163

veya

>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='little')
3148270713

byte-string'inizin endianitesine göre .

Bu aynı zamanda keyfi uzunluktaki tamsayıları test etmek ve belirterek ikinin tamamlayıcı işaretli tam sayıları için de geçerlidir signed=True. İçin dokümanlara bakınfrom_bytes .


@eri ne kadar yavaş? Yapıyı kullanıyordum ama py3'e gittiğimde int.from_bytes'e dönüştüm. Herhangi bir speedup açığız böylece seri veri almak gibi her ms bu yöntemi çağırıyorum.
Şuna

@Naib, cpu'mdaki os.urandom(4)bayt ** 1,4 µs ** (yapı) vs ** 2,3 µs ** (int.from_bytes) için. python 3.5.2
eri

5
@eri CRC yöntemlerini bir çift değerlendirmek için kullanılan bir timeit komut dosyası yeniden dirildi. Dört tur 1) yapı 2) int.from_bytes 3) # 1 olarak ama cython derlendi, 4) # 2 olarak ama cython derlendi. Yapısı için 330ns, int için 1.14us (cython belki 20ns hızlanma verdi ...) geri dönüyorum gibi görünüyor :) Bu erken optimizasyon değil, özellikle bir milyon numune göndermek için bazı kötü darboğazlara çarpıyorum -process ve parçaları çalıyor.
Naib

66

Greg'in dediği gibi, ikili değerlerle uğraşıyorsanız struct kullanabilirsiniz, ancak sadece bir "onaltılık sayı" nız varsa, ancak bayt biçiminde ise şu şekilde dönüştürmek isteyebilirsiniz:

s = 'y\xcc\xa6\xbb'
num = int(s.encode('hex'), 16)

... bu şununla aynı:

num = struct.unpack(">L", s)[0]

... ancak herhangi bir sayıda bayt için çalışması dışında.


3
"ikili değerler" ile "onaltılık sayı" arasındaki, ancak bayt biçimindeki fark tam olarak nedir ???????

Bkz. "Yardım yapısı". Örneğin. "001122334455" .decode ('hex') struct kullanılarak bir sayıya dönüştürülemez.
James Antill

3
Bu arada, bu cevap tamsayının big-endian bayt sırasına göre kodlandığını varsayar. Little-endian sipariş için int(''.join(reversed(s)).encode('hex'), 16)
şunları yapın

1
iyi ama bu yavaş olacak! Sanırım Python'da kod yazmanız önemli değil.
MattCochrane

8

Ben int, hex ve bayt arasında veri dönüştürmek için aşağıdaki işlevi kullanın.

def bytes2int(str):
 return int(str.encode('hex'), 16)

def bytes2hex(str):
 return '0x'+str.encode('hex')

def int2bytes(i):
 h = int2hex(i)
 return hex2bytes(h)

def int2hex(i):
 return hex(i)

def hex2int(h):
 if len(h) > 1 and h[0:2] == '0x':
  h = h[2:]

 if len(h) % 2:
  h = "0" + h

 return int(h, 16)

def hex2bytes(h):
 if len(h) > 1 and h[0:2] == '0x':
  h = h[2:]

 if len(h) % 2:
  h = "0" + h

 return h.decode('hex')

Kaynak: http://opentechnotes.blogspot.com.au/2014/04/convert-values-to-from-integer-hex.html


6
import array
integerValue = array.array("I", 'y\xcc\xa6\xbb')[0]

Uyarı: Yukarıdakiler platforma özgüdür. Hem "I" belirleyicisi hem de string-> int dönüşümünün endianitesi özel Python uygulamanıza bağlıdır. Ancak aynı anda birçok tamsayıyı / dizeyi dönüştürmek istiyorsanız, dizi modülü bunu hızlı bir şekilde yapar.


5

Python 2.x olarak, biçim belirteçleri kullanabilirsiniz <Bimzasız bayt için ve <bimzalanan bayt için struct.unpack/ ' struct.pack.

Örneğin:

Let x='\xff\x10\x11'

data_ints = struct.unpack('<' + 'B'*len(x), x) # [255, 16, 17]

Ve:

data_bytes = struct.pack('<' + 'B'*len(data_ints), *data_ints) # '\xff\x10\x11'

Bu *gerekli!

Görmek Biçim belirteçlerinin listesi için https://docs.python.org/2/library/struct.html#format-characters .


3
>>> reduce(lambda s, x: s*256 + x, bytearray("y\xcc\xa6\xbb"))
2043455163

Test 1: ters:

>>> hex(2043455163)
'0x79cca6bb'

Test 2: Bayt sayısı> 8:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAAA"))
338822822454978555838225329091068225L

Test 3: Bir artırım:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAAB"))
338822822454978555838225329091068226L

Test 4: Bir bayt ekleyin, 'A' deyin:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))
86738642548474510294585684247313465921L

Test 5: 256'ya bölün:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))/256
338822822454978555838225329091068226L

Sonuç, beklendiği gibi Test 4'ün sonucuna eşittir.


1

Python 2.x altında çalışacak keyfi uzunluk bayt dizileri için bir çözüm bulmakta zorlanıyordum. Sonunda bunu yazdım, bir dize dönüştürme gerçekleştirdiği için biraz hileli, ama işe yarıyor.

Python 2.x işlevi, keyfi uzunluk

def signedbytes(data):
    """Convert a bytearray into an integer, considering the first bit as
    sign. The data must be big-endian."""
    negative = data[0] & 0x80 > 0

    if negative:
        inverted = bytearray(~d % 256 for d in data)
        return -signedbytes(inverted) - 1

    encoded = str(data).encode('hex')
    return int(encoded, 16)

Bu işlevin iki gereksinimi vardır:

  • Girişin bir dataolması gerekir bytearray. İşlevi şu şekilde çağırabilirsiniz:

    s = 'y\xcc\xa6\xbb'
    n = signedbytes(s)
  • Verilerin big-endian olması gerekir. Küçük bir endian değeriniz varsa, önce onu tersine çevirmelisiniz:

    n = signedbytes(s[::-1])

Tabii ki, bu sadece keyfi uzunluk gerekiyorsa kullanılmalıdır. Aksi takdirde, daha standart yollarla devam edin (örn. struct).


1

int.from_bytes> = 3.2 sürümündeyseniz en iyi çözümdür. "Struct.unpack" çözümü bir dize gerektirir, bu nedenle bayt dizileri için geçerli olmaz. İşte başka bir çözüm:

def bytes2int( tb, order='big'):
    if order == 'big': seq=[0,1,2,3]
    elif order == 'little': seq=[3,2,1,0]
    i = 0
    for j in seq: i = (i<<8)+tb[j]
    return i

hex (bayt2int ([0x87, 0x65, 0x43, 0x21])) '0x87654321' değerini döndürür.

Büyük ve küçük endianiteye sahiptir ve 8 bayt için kolayca değiştirilebilir


1

Yukarıda belirtildiği gibi unpack, yapı işlevini kullanmak iyi bir yoldur. Kendi işlevinizi uygulamak istiyorsanız başka bir çözüm daha var:

def bytes_to_int(bytes):
    result = 0
    for b in bytes:
        result = result * 256 + int(b)
return result

Bu, bayta dönüştürülen negatif sayı için çalışmaz.
Maria

1

Python 3'te, bir bayt dizesini bir tamsayı listesine (0..255) kolayca dönüştürebilirsiniz.

>>> list(b'y\xcc\xa6\xbb')
[121, 204, 166, 187]

0

Bir süredir kullanıyorum dizi.array kullanarak terbiyeli hızlı bir yöntem:

önceden tanımlanmış değişkenler:

offset = 0
size = 4
big = True # endian
arr = array('B')
arr.fromstring("\x00\x00\xff\x00") # 5 bytes (encoding issues) [0, 0, 195, 191, 0]

int: (okumak)

val = 0
for v in arr[offset:offset+size][::pow(-1,not big)]: val = (val<<8)|v

int'den: (yazma)

val = 16384
arr[offset:offset+size] = \
    array('B',((val>>(i<<3))&255 for i in range(size)))[::pow(-1,not big)]

Ancak bunlar daha hızlı olabilir.

DÜZENLEME:
Bazı sayılar için, aşağıdakilerle karşılaştırıldığında okunan ortalamaları gösteren bir performans testi (Anaconda 2.3.0) reduce():

========================= byte array to int.py =========================
5000 iterations; threshold of min + 5000ns:
______________________________________code___|_______min______|_______max______|_______avg______|_efficiency
⣿⠀⠀⠀⠀⡇⢀⡀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡀⠀⢰⠀⠀⠀⢰⠀⠀⠀⢸⠀⠀⢀⡇⠀⢀⠀⠀⠀⠀⢠⠀⠀⠀⠀⢰⠀⠀⠀⢸⡀⠀⠀⠀⢸⠀⡇⠀⠀⢠⠀⢰⠀⢸⠀
⣿⣦⣴⣰⣦⣿⣾⣧⣤⣷⣦⣤⣶⣾⣿⣦⣼⣶⣷⣶⣸⣴⣤⣀⣾⣾⣄⣤⣾⡆⣾⣿⣿⣶⣾⣾⣶⣿⣤⣾⣤⣤⣴⣼⣾⣼⣴⣤⣼⣷⣆⣴⣴⣿⣾⣷⣧⣶⣼⣴⣿⣶⣿⣶
    val = 0 \nfor v in arr: val = (val<<8)|v |     5373.848ns |   850009.965ns |     ~8649.64ns |  62.128%
⡇⠀⠀⢀⠀⠀⠀⡇⠀⡇⠀⠀⣠⠀⣿⠀⠀⠀⠀⡀⠀⠀⡆⠀⡆⢰⠀⠀⡆⠀⡄⠀⠀⠀⢠⢀⣼⠀⠀⡇⣠⣸⣤⡇⠀⡆⢸⠀⠀⠀⠀⢠⠀⢠⣿⠀⠀⢠⠀⠀⢸⢠⠀⡀
⣧⣶⣶⣾⣶⣷⣴⣿⣾⡇⣤⣶⣿⣸⣿⣶⣶⣶⣶⣧⣷⣼⣷⣷⣷⣿⣦⣴⣧⣄⣷⣠⣷⣶⣾⣸⣿⣶⣶⣷⣿⣿⣿⣷⣧⣷⣼⣦⣶⣾⣿⣾⣼⣿⣿⣶⣶⣼⣦⣼⣾⣿⣶⣷
                  val = reduce( shift, arr ) |     6489.921ns |  5094212.014ns |   ~12040.269ns |  53.902%

Bu bir ham performans testidir, bu yüzden endian pow-flip dışarıda bırakılır.
Gösterilen shiftişlev for döngüsü ile aynı kaydırma veya kaydırma işlemini uygular ve arrtıpkı array.array('B',[0,0,255,0])yanında en hızlı yinelemeli performansa sahip olduğu gibidir dict.

Muhtemelen verimliliğin ortalama süreye doğrulukla ölçüldüğünü de belirtmeliyim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.