2009-10-27 6 views
5

Es wurde vor kurzem gefragt how to do a file slurp in python und die akzeptierte Antwort vorgeschlagen etwas wie:Python Datei Slurp w/Endian-Umwandlung

with open('x.txt') as x: f = x.read() 

Wie würde ich mich darüber, die Datei in und wandelt die Endian-Darstellung des lesen tun Daten?

Zum Beispiel habe ich eine 1-GB-Binärdatei, die nur eine Reihe von einzelnen Präzision Floats als Big Endian gepackt ist und ich möchte es in Little Endian konvertieren und dump in ein numpy Array. Unten ist die Funktion, die ich geschrieben habe, um dies zu erreichen, und ein echter Code, der sie aufruft. Ich benutze struct.unpack die Endian-Konvertierung und versuchte, alles zu beschleunigen, indem Sie mmap verwenden.

Meine Frage ist dann, verwende ich den slurp korrekt mit mmap und struct.unpack? Gibt es einen saubereren, schnelleren Weg, dies zu tun? Gerade jetzt, was ich habe funktioniert, aber ich würde wirklich gerne lernen, wie man das besser macht.

Vielen Dank im Voraus!

#!/usr/bin/python 
from struct import unpack 
import mmap 
import numpy as np 

def mmapChannel(arrayName, fileName, channelNo, line_count, sample_count): 
    """ 
    We need to read in the asf internal file and convert it into a numpy array. 
    It is stored as a single row, and is binary. Thenumber of lines (rows), samples (columns), 
    and channels all come from the .meta text file 
    Also, internal format files are packed big endian, but most systems use little endian, so we need 
    to make that conversion as well. 
    Memory mapping seemed to improve the ingestion speed a bit 
    """ 
    # memory-map the file, size 0 means whole file 
    # length = line_count * sample_count * arrayName.itemsize 
    print "\tMemory Mapping..." 
    with open(fileName, "rb") as f: 
     map = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) 
     map.seek(channelNo*line_count*sample_count*arrayName.itemsize) 

     for i in xrange(line_count*sample_count): 
      arrayName[0, i] = unpack('>f', map.read(arrayName.itemsize))[0] 

     # Same method as above, just more verbose for the maintenance programmer. 
     #  for i in xrange(line_count*sample_count): #row 
     #   be_float = map.read(arrayName.itemsize) # arrayName.itemsize should be 4 for float32 
     #   le_float = unpack('>f', be_float)[0] # > for big endian, < for little endian 
     #   arrayName[0, i]= le_float 

     map.close() 
    return arrayName 

print "Initializing the Amp HH HV, and Phase HH HV arrays..." 
HHamp = np.ones((1, line_count*sample_count), dtype='float32') 
HHphase = np.ones((1, line_count*sample_count), dtype='float32') 
HVamp = np.ones((1, line_count*sample_count), dtype='float32') 
HVphase = np.ones((1, line_count*sample_count), dtype='float32') 



print "Ingesting HH_Amp..." 
HHamp = mmapChannel(HHamp, 'ALPSRP042301700-P1.1__A.img', 0, line_count, sample_count) 
print "Ingesting HH_phase..." 
HHphase = mmapChannel(HHphase, 'ALPSRP042301700-P1.1__A.img', 1, line_count, sample_count) 
print "Ingesting HV_AMP..." 
HVamp = mmapChannel(HVamp, 'ALPSRP042301700-P1.1__A.img', 2, line_count, sample_count) 
print "Ingesting HV_phase..." 
HVphase = mmapChannel(HVphase, 'ALPSRP042301700-P1.1__A.img', 3, line_count, sample_count) 

print "Reshaping...." 
HHamp_orig = HHamp.reshape(line_count, -1) 
HHphase_orig = HHphase.reshape(line_count, -1) 
HVamp_orig = HVamp.reshape(line_count, -1) 
HVphase_orig = HVphase.reshape(line_count, -1) 
+0

ich für jemand anderen dazu hinzufügen wollte, die diesen Beitrag nützlich findet. Das Ausführen des ursprünglichen Codes dauert ungefähr 80 Sekunden. Die Lösung von Alex Martelli und JF Sebastian läuft weniger als eine Sekunde. Das Programm, das diese Funktion aufruft, tut dies so oft. Die Laufzeit ist damit deutlich gesunken. Danke euch beiden für die Hilfe und dafür, mir etwas beizubringen =) – Foofy

Antwort

6
with open(fileName, "rb") as f: 
    arrayName = numpy.fromfile(f, numpy.float32) 
arrayName.byteswap(True) 

ziemlich schwer zu schlagen ;-) für Geschwindigkeit und Prägnanz bedienen. Für Byteswap siehe here (das True Argument bedeutet "do it in Place"); für Fromfile siehe here.

Dies funktioniert wie auf Little-Endian-Maschinen (da die Daten Big-Endian sind, wird die Byteswap benötigt). Sie können testen, ob das der Fall ist, die byteswap bedingt, ändern Sie die letzte Zeile von einem unbedingten Aufruf byteswap in, zum Beispiel zu tun:

if struct.pack('=f', 2.3) == struct.pack('<f', 2.3): 
    arrayName.byteswap(True) 

dh ein Anruf auf einen Test von little-Endian-bedingte byteswap .

+0

das ist bemerkenswert unkompliziert.danke was ist komisch ist ich hatte diese gesehen, als ich versuchte herauszufinden, wie man das macht, aber es hat einfach nicht aus irgendeinem Grund registriert. kommt mit Erfahrung ich nehme an =) – Foofy

+2

numpy.float32 hat native Byte-Reihenfolge, die nicht immer Big-Endian sein kann. http://StackOverflow.com/Questions/1632673/python-file-slurp-w-endian-conversion/1633525#1633525 – jfs

+0

In der Tat wird es meist Little-Endian sein, aber wenn Sie z. Auf einer Power-PC-Maschine wird es Big-Endian sein (wenn das ein Problem ist, unterlassen Sie einfach den Byteswap-Aufruf - lassen Sie mich die Antwort editieren, um dieses Bit hinzuzufügen). –

0

Sie könnten Coble zusammen ein ASM based solutionCorePy verwenden. Ich frage mich allerdings, ob Sie vielleicht in der Lage sind, genügend Leistung von einem anderen Teil Ihres Algorithmus zu erhalten. I/O und Manipulationen an 1 GB großen Stücken von Daten werden eine Weile dauern, wie auch immer Sie es schneiden.

Eine andere Sache, die Sie hilfreich finden könnten wäre, zu C zu wechseln, sobald Sie den Algorithmus in Python prototypisiert haben. Ich habe dies für Manipulationen an einem ganzen Welt DEM (Höhe) -Datensatz einmal getan. Die ganze Sache war viel erträglicher, sobald ich von der interpretierten Schrift wegkam.

0

Ich würde erwarten, dass so etwas wie dies schneller

arrayName[0] = unpack('>'+'f'*line_count*sample_count, map.read(arrayName.itemsize*line_count*sample_count)) 

Bitte nicht map als Variablenname

7

leicht modifiziert @Alex Martelli's answer:

arr = numpy.fromfile(filename, numpy.dtype('>f4')) 
# no byteswap is needed regardless of endianess of the machine 
+0

Wahrscheinlich möchten Sie das mit .astype kombinieren, um es in ein natives Format zu bringen, z. 'arr = numpy.fromfile (Dateiname, numpy.dtype ('> f4')). astype (np.float32)' – RolKau

+0

@RolKau-Nr. Versuchen Sie, Code mit und ohne den Anruf auszuführen, und sehen Sie, was passiert. – jfs

+0

@JFSebastian Vielleicht habe ich daraus zu weit von dem tatsächlichen Fall in der Frage extrapoliert, aber betrachte den Python-Code: 'b = bytearray ([0, 0, 0, 1]); a = numpy.frombuffer (b, dtype = numpy.dtype ('> i4')); c = a.Art (numpy.int32); (a.tostring(), c.tostring()) '. Auf meiner Plattform (Linux, Python 2.7, x86_64) bekomme ich die Ergebnisse a = '\ x00 \ x00 \ x00 \ x01', c =' \ x01 \ x00 \ x00 \ x00', die ich interpretiere, dass nur c intern gespeichert wurde als Little-Endian. – RolKau