2015-09-09 13 views
12

In python, mi è stato assegnato un numero intero a 64 bit. Questo intero è stato creato prendendo diversi diversi numeri interi a 8 bit e mischiandoli insieme in un gigantesco numero intero a 64 bit. È mio compito separarli di nuovo.Convertire un numero intero a 64 bit in 8 interi interi da 1 byte in python

Ad esempio:

Source number: 2592701575664680400 
Binary (64 bits): 0010001111111011001000000101100010101010000101101011111000000000 
int 1: 00100011 (35) 
int 2: 11111011 (251) 
int 3: 00100000 (32) 
int 4: 01011000 (88) 
int 5: 10101010 (170) 
int 6: 00010110 (22) 
int 7: 10111110 (190) 
int 8: 00000000 (0) 

Quindi quello che vorrei fare è prendere il mio numero sorgente 2592701575664680373 e restituisce un array di lunghezza 8, in cui ogni int nella matrice sono gli interi sopra elencati.

Stavo per usare struct, ma per essere perfettamente onesto, leggere il documentation non ha reso abbastanza chiaro esattamente come avrei realizzato.

+0

Hai provato 'divmod()'? – lenz

+0

Dang, sei corretto @PadraicCunningham. Stavo usando uno strumento veloce e sporco che non supportava numeri abbastanza grandi, e ha troncato quella parte finale con gli 0. Ora che ho eseguito 'bin = '{0: 064b}'. Format (source)' Vedo che sei corretto. – JHixson

+0

Il fatto 'n' è dispari e non ce n'era uno alla fine mi ha confuso –

risposta

6

In Python 2.x, struct.pack restituisce una stringa di byte. È facile convertirlo in un array di numeri interi.

>>> bytestr = struct.pack('>Q', 2592701575664680400) 
>>> bytestr 
'#\xfb X\xaa\x16\xbd\xd0' 
>>> [ord(b) for b in bytestr] 
[35, 251, 32, 88, 170, 22, 189, 208] 

Il modulo struct in pitone viene utilizzato per la conversione da oggetto pitone a stringhe di byte, tipicamente imballati in base alle leggi struttura imballaggio C. struct.pack accetta un identificatore di formato (una stringa che descrive come devono essere disposti i byte della struttura) e alcuni dati python e li impacchetta in una stringa di byte. struct.unpack esegue l'inverso, prendendo un identificatore di formato e una stringa di byte e restituendo una tupla di dati decompressi ancora una volta nel formato degli oggetti python.

Lo specificatore di formato utilizzato ha due parti. Il carattere principale specifica la endianità (ordine byte) della stringa. I seguenti caratteri specificano i tipi dei campi della struttura che viene compresso o decompresso. Quindi, '>Q' significa impacchettare i dati dati come big-endian unsigned long long. Per ottenere i byte nell'ordine opposto, è possibile utilizzare < invece per little-endian.

L'operazione finale è una comprensione di lista che itera sui caratteri della stringa di byte e utilizza la funzione incorporata ord per ottenere la rappresentazione intera di quel carattere.

Nota finale: Python in realtà non ha un concetto di dimensione intera. In 2.x, c'è int che è limitato a 32 bit e long di dimensioni illimitate. In 3.x quei due sono stati unificati in un unico tipo. Quindi, anche se questa operazione garantisce di fornire interi che occupano solo un byte, notando che Python costringerà gli interi risultanti a rimanere in questo modo se li si utilizza in altre operazioni.

+0

Grazie mille per la spiegazione! Questo non solo risolve il mio problema, ma mi sento molto più sicuro della mia capacità di usare il modulo 'struct' d'ora in poi. – JHixson

+0

@JHixson puoi ringraziare zstewart che ha aggiunto l'intera spiegazione dopo aver risposto con il codice. –

2
bn = "0010001111111011001000000101100010101010000101101011111000000000" 

print([int(bn[i:i+8], 2) for i in range(0,len(bn), 8)]) 
[35, 251, 32, 88, 170, 22, 190, 0] 

Se si utilizza la rappresentazione binaria di n allora l'uscita sarebbe diverso:

n = 2592701575664680373 
bn = bin(n) 

print([int(bn[i:i+8], 2) for i in range(0,len(bn), 8)]) 
[35, 251, 32, 88, 170, 22, 189, 181] 

Alcune timing:

In [16]: %%timeit             
numbers = list((n >> i) & 0xFF for i in range(0,64,8)) 
list(reversed(numbers)) 
    ....: 
100000 loops, best of 3: 2.97 µs per loop 

In [17]: timeit [(n >> (i * 8)) & 0xFF for i in range(7, -1, -1)] 
1000000 loops, best of 3: 1.73 µs per loop 

In [18]: %%timeit             
bn = bin(n) 
[int(bn[i:i+8], 2) for i in range(0,len(bn), 8)] 
    ....: 
100000 loops, best of 3: 3.96 µs per loop 

Si può anche solo divmod:

out = [] 
for _ in range(8): 
    n, i = divmod(n, 256) 
    out.append(i) 
out = out[::-1] 

Che è quasi come e fficient:

In [31]: %%timeit 
    ....: n = 2592701575664680411 
    ....: out = [] 
    ....: for _ in range(8): 
    ....:  n, i = divmod(n, 1 << 8) 
    ....:  out.append(i) 
    ....: out[::-1] 
    ....: 
100000 loops, best of 3: 2.35 µs per loop 

C'è molto poco vantaggio nel bit spostando con Python, sarei più propenso a usare quello che si e gli altri a trovare più leggibile.

8

Soluzione

Soluzione senza convertire il numero in una stringa:

x = 0b0010001111111011001000000101100010101010000101101011111000000000 

numbers = list((x >> i) & 0xFF for i in range(0,64,8)) 
print(numbers)     # [0, 190, 22, 170, 88, 32, 251, 35] 
print(list(reversed(numbers))) # [35, 251, 32, 88, 170, 22, 190, 0] 

Spiegazione

Qui ho usato list comprehension, facendo un cappio con incrementi di 8 su i. Quindi i assume i valori 0, 8, 16, 24, 32, 40, 48, 56. Ogni volta, l'operatore bithift >> sposta temporaneamente il numero x in basso di i bit. Questo equivale a dividere per 256^i.

Così il numero risultante è:

i = 0: 0010001111111011001000000101100010101010000101101011111000000000 
i = 8:   00100011111110110010000001011000101010100001011010111110 
i = 16:     001000111111101100100000010110001010101000010110 
i = 24:       0010001111111011001000000101100010101010 
i = 32:         00100011111110110010000001011000 
i = 40:           001000111111101100100000 
i = 48:             0010001111111011 
i = 56:               00100011 

Con usig & 0xFF, ho selezionare gli ultimi 8 bit di questo numero. Esempio:

x >> 48:   001000111111101100100000 
0xff:        11111111 
(x >> 48) & 0xff: 000000000000000000100000 

Poiché gli zeri iniziali non sono importanti, si ha il numero desiderato.

Il risultato viene convertito in un elenco e stampato in ordine normale e inverso (come OP voluto).

prestazioni

ho confrontato i tempi di questo risultato alle altre soluzioni proposte in questa discussione:

In: timeit list(reversed([(x >> i) & 0xFF for i in range(0,64,8)])) 
100000 loops, best of 3: 13.9 µs per loop 

In: timeit [(x >> (i * 8)) & 0xFF for i in range(7, -1, -1)] 
100000 loops, best of 3: 11.1 µs per loop 

In: timeit [(x >> i) & 0xFF for i in range(63,-1,-8)] 
100000 loops, best of 3: 10.2 µs per loop 

In: timeit reversed(struct.unpack('8B', struct.pack('Q', x))) 
100000 loops, best of 3: 3.22 µs per loop 

In: timeit reversed(struct.pack('Q', x)) 
100000 loops, best of 3: 2.07 µs per loop 

Risultato: la mia soluzione non è il più veloce! Attualmente, usare direttamente struct (come proposto da Mark Ransom) sembra essere lo snippet più veloce.

+1

Puoi anche '[(n >> (i * 8)) e 0xFF per i in range (7, -1, -1)]' e dimenticare l'inversione –

+0

Per qualche motivo, ottengo risultati di temporizzazione diversi. Sto usando iPython 2.0.0 su Python 3.4.2, 32 bit. Su una macchina Windows a 64 bit. – jojonas

+0

C'è una risposta semplice, non stai facendo nulla nel primo codice, hai un'espressione di generatore all'interno della lista –

2

Ecco una versione con struct:

import struct 
n = 2592701575664680400 
bytes = struct.unpack('8B', struct.pack('Q', n)) 

Il bytes vengono restituiti in ordine opposto che hai mostrato nella sua interrogazione.

Ecco le statistiche di performance:

python -m timeit -s "import struct" "struct.unpack('8B', struct.pack('Q', 2592701575664680400))" 
1000000 loops, best of 3: 0.33 usec per loop 

Sul mio computer, questo è tre volte più veloce rispetto alla soluzione di byte-shifting.

+1

È possibile controllare l'ordine in cui i byte vengono restituiti specificando un ordine di byte per il numero intero a 64 bit (ad esempio big-endian, con '>'). – Blckknght

Problemi correlati