Python, hoe een 32-bit integer in byte array te zetten

Meestal doe ik dit soort dingen in C ++, maar ik gebruik Python om een ​​snel script te schrijven en ik ben tegen een muur aangelopen.

Als ik een binaire lijst heb (of wat dan ook, bewaart python het resultaat van een "fread"). Ik kan de individuele bytes erin openen met: buffer [0], buffer [1], enz.

Ik moet de bytes [8-11] wijzigen om een ​​nieuwe 32-bits bestandsgrootte te behouden (lees: er is al een bestandsgrootte, ik moet het bijwerken). In C ++ zou ik alleen maar een verwijzing naar de locatie krijgen en deze weggooien om het gehele getal op te slaan, maar met python besefte ik opeens dat ik geen idee heb hoe ik zoiets moet doen.

Hoe kan ik 4 bytes in mijn buffer op een specifieke locatie bijwerken om de waarde van een integer in python te behouden?

Bewerken

Ik ga er meer aan toevoegen omdat ik er niet uit kan komen met de oplossingen (hoewel ik zie dat ze op de goede weg zijn).

Allereerst, ik ben op python 2.4 (en kan geen upgrade uitvoeren, servers van grote bedrijven) - zodat mijn opties blijkbaar worden beperkt. Sorry dat ik dat eerder niet heb vermeld, ik was me er niet van bewust dat het zoveel minder functies had.

Ten tweede, laten we dit ultraeenvoudig maken.

Laten we zeggen dat ik een binair bestand heb met de naam 'myfile.binary' met de vijfbytes inhoud '4C53535353' in hexadecimaal - dit komt overeen met de ascii-representaties voor letters "L en 4xS" die alleen in het bestand staan.

Als ik doe:

f = open('myfile.binary', 'rb')
contents = f.read(5)

inhoud zou (uit het antwoord van Sven Marnach) een vijf-byte onveranderlijke reeks moeten houden.

Alleen met Python 2.4-faciliteiten, hoe kon ik de 4 S's in 'inhoud' wijzigen in een willekeurige geheel getal? D.w.z. geef me een regel code waarmee byte-indexen [1-4] het 32-bits gehele getal 'myint' kunnen bevatten met waarde 12345678910.

6
Bedankt, ik ben blij om een ​​nieuwe reeks of byte array te maken als dat het geval is. Hoe kan ik een aanpasbare bytearray maken met dezelfde contnets en het bereik [1-4] daarvan bijwerken om de binaire weergave van mijn gehele getal te evenaren?
toegevoegd de auteur John Humphreys - w00te, de bron
U kunt de inhoud niet wijzigen, omdat de reeks onveranderlijk is ... u kunt een nieuwe reeks maken of een veranderlijke container zoals bytearray gebruiken.
toegevoegd de auteur hochl, de bron
Ik heb mijn bericht hieronder uitgebreid en een werkend voorbeeld toegevoegd voor Python 2.4 :-)
toegevoegd de auteur hochl, de bron

5 antwoord

Wat je nodig hebt, is deze functie:

struct.pack_into(fmt, buffer, offset, v1, v2, ...)

Het is gedocumenteerd op http://docs.python.org/library/struct.html dichtbij de top.

Voorbeeldcode:

import struct
import ctypes

data=ctypes.create_string_buffer(10)
struct.pack_into(">i", data, 5, 0x12345678)
print list(data)

Similar posting: Python: How to pack different types of data into a string buffer using struct.pack_into

EDIT: Added a Python 2.4 compatible example:

import struct

f=open('myfile.binary', 'rb')
contents=f.read(5)
data=list(contents)
data[0:4]=struct.pack(">i", 0x12345678)
print data
9
toegevoegd
Voor iedereen probeerde ik het uit te werken door de pakketpagina en de geboden oplossingen te lezen. Ik schijn veel te missen omdat ik python 2.4 heb in plaats van 2.5. Ik heb een sterk vereenvoudigde versie van mijn probleem gegeven in een bewerking in mijn bericht hierboven - als je dat kunt beantwoorden met een regel code, zal ik helemaal blij zijn :)
toegevoegd de auteur John Humphreys - w00te, de bron
1. Ik zou willen voorstellen om de ingebouwde bytearray te gebruiken om de veranderbare buffer te maken in plaats van ctypes.create_string_buffer .
toegevoegd de auteur Sven Marnach, de bron
Hmmm. Dat klopt, ik vermeed alleen de functie pack_into ... beschamend. Laten we eens kijken wat er in 2.4 was :)
toegevoegd de auteur hochl, de bron
Oké, ik denk dat het nu werkt, probeerde het op een VM met Python 2.4. Andere bezwaren?
toegevoegd de auteur hochl, de bron
Ik vrees dat je 2.4-compatibele versie bytearray niet kan gebruiken zoals deze in 2.6 werd geïntroduceerd.
toegevoegd de auteur Scott Griffiths, de bron

Bekijk de module Structuur . U hebt de functie pack nodig.

BEWERK:

De code:

import struct

s = "LSSSS" # your string
s = s[0] + struct.pack('

Output:

L╙☻ЦI

struct.pack should be available in Python2.4.

Uw nummer "12345678910" kan niet in 4 bytes worden ingepakt, ik heb het een beetje ingekort.

4
toegevoegd
zie reactie op antwoord van hochl
toegevoegd de auteur John Humphreys - w00te, de bron
update het antwoord
toegevoegd de auteur Andriy Tylychko, de bron

Het resultaat van file.read() is een tekenreeks in Python en is onveranderlijk. Afhankelijk van de context van de taak die u probeert te volbrengen, zijn er verschillende oplossingen voor het probleem.

Een daarvan gebruikt de array -module : lees het bestand rechtstreeks als een array van 32-bits gehele getallen. U kunt deze array wijzigen en deze terug naar het bestand schrijven.

with open("filename") as f:
    f.seek(0, 2)
    size = f.tell()
    f.seek(0)
    data = array.array("i")
    assert data.itemsize == 4
    data.fromfile(f, size//4)
data[2] = new_value
# use data.tofile(g) to write the data back to a new file g
2
toegevoegd
zie reactie op antwoord van hochl
toegevoegd de auteur John Humphreys - w00te, de bron

U kunt de numpy-module installeren, die vaak wordt gebruikt voor wetenschappelijk computergebruik.

read_data = numpy.fromfile (bestand = id, dtype = numpy.uint32)

Open vervolgens de gegevens op de gewenste locatie en breng uw wijzigingen aan.

2
toegevoegd
zie reactie op antwoord van hochl
toegevoegd de auteur John Humphreys - w00te, de bron

Het volgende is slechts een demonstratie om te begrijpen wat er echt gebeurt wanneer de vier bytes worden omgezet in een geheel getal. Stel dat je een nummer hebt: 15213

Decimal: 15213
Binary: 0011 1011 0110 1101
Hex: 3 B 6 D

Op kleine endian-systemen (bijv. X86-machines), kan dit aantal worden weergegeven door gebruik te maken van een lengte-4 bytearray als: b "\ x6d \ x3b \ x00 \ x00" of b "m; \ x00 \ x00 " wanneer u het op het scherm afdrukt, om de vier bytes in een geheel getal te converteren, doen we gewoon een beetje een basisconversie, wat in dit geval is:

sum(n*(256**i) for i,n in enumerate(b"\x6d\x3b\x00\x00"))

Dit geeft je het resultaat: 15213

1
toegevoegd