2015-10-26 7 views
7

Ich habe eine Binärdatei, die mit einem Python-Code erstellt wurde. Dieser Code erstellt hauptsächlich eine Reihe von Aufgaben, um eine Reihe von Datendateien vorzuverarbeiten. Ich möchte jetzt diese Binärdatei in Fortran lesen. Der Inhalt der Binärdatei ist die Koordinaten von Punkten in einem einfachen Format zB: Anzahl der Punkte, x0, y0, z0, x1, y1, z1, ...Lesen einer Binärdatei in Fortran, die von einem Python-Code erstellt wurde

Diese binären Dateien wurden mit dem 'tofile' erstellt Funktion in numpy. Ich habe den folgenden Code in Fortran bisher:

integer:: intValue 
double precision:: dblValue 
integer:: counter 
integer:: check 
open(unit=10, file='file.bin', form='unformatted', status='old', access='stream') 

counter = 1 

do 

    if (counter == 1) then 
    read(unit=10, iostat=check) intValue 
    if (check < 0) then 
     print*,"End Of File" 
     stop 
    else if (check > 0) then 
     print*, "Error Detected" 
     stop 
    else if (check == 0) then 
     counter = counter + 1 
     print*, intValue 
    end if 
    else if (counter > 1) then 
    read(unit=10, iostat=check) dblValue 
    if (check < 0) then 
     print*,"End Of File" 
     stop 
    else if (check > 0) then 
     print*, "Error Detected" 
     stop 
    else if (check == 0) then 
     counter = counter + 1 
     print*,dblValue 
    end if 
    end if 

end do 

close(unit=10) 

Dies ist leider nicht funktioniert, und ich garbage Zahlen (z 6.4731191026611484E + 212, 2.2844499004808491E-279 etc.). Könnte jemand Hinweise geben, wie man das richtig macht? Auch, was wäre eine gute Möglichkeit zum Schreiben und Lesen von Binärdateien austauschbar zwischen Python und Fortran - wie es scheint, wird dies eine der Anforderungen meiner Anwendung sein.

Dank

+1

Ich fügte eine nette und detaillierte Antwort hinzu und sagte, dass du 'access = stream' verwenden sollst :) Ich habe gerade gemerkt, dass du das schon machst, also habe ich meine Antwort vorerst gelöscht. Also, Frage: Sind Sie sicher, dass die Bytegröße Ihres Pythons 'int' und Ihres Fortran' integer' gleich sind? Sie sollten beide überprüfen. Wenn es ein einzelnes Diskrepanzbyte gibt, führt die Fehlausrichtung der Daten nach dem Lesen zu Müll. Welchen Fortran Compiler benutzen Sie? Wie erklärst du deine 'Integer's? Was ist der spezifische Typ deines Pythons? –

+0

Wenn Sie wirklich verzweifelt sind, können Sie versuchen, die (angeblich) gleiche Dummy-Binärdatei sowohl mit Fortran als auch mit Python zu erzeugen, und dann den Hex-Dump der beiden Dateien betrachten, um zu sehen, was los ist. Auch meine frühere Frage nach den "Integer" -Größen gilt offensichtlich auch für die involvierten "Doubles". Und selbst wenn die Typen auschecken, kann es immer noch ein Endianess-Problem geben, wenn Sie die beiden Codes auf zwei sehr unterschiedlichen Rechnern verwenden. –

+1

In Bezug auf die Frage der Austauschbarkeit: Ich würde eher Metadaten wie die Anzahl der Punkte in eine separate ASCII-Header-Datei, die leicht gelesen werden kann, und nur Daten der gleichen Art in eine einzige Binärdatei, dies ermöglicht auch für eine faire einfache Umwandlung von Endianness. – haraldkl

Antwort

1

Hier ist ein einfaches Beispiel dafür, wie mit numpy erzeugten Daten zu ergreifen, um die binäre Art und Weise Fortran.

I berechnet 360 Werte von sin auf [0,2π),

#!/usr/bin/env python3 
import numpy as np 

with open('sin.dat', 'wb') as outfile: 
    np.sin(np.arange(0., 2*np.pi, np.pi/180., 
        dtype=np.float32)).tofile(outfile) 

exportiert dass mit tofile zu Binärdatei 'sin.dat', die eine Größe von 1440 bytes (360 * sizeof(float32)) hat, lesen Sie die Datei mit diesem Fortran95 (gfortran O3 -Wall -pedantic) Programm, das in [0,2π) 1. - (val**2 + cos(x)**2) für x ausgibt,

program numpy_import 
    integer,   parameter     :: REAL_KIND = 4 
    integer,   parameter     :: UNIT = 10 
    integer,   parameter     :: SAMPLE_LENGTH = 360 
    real(REAL_KIND), parameter     :: PI = acos(-1.) 
    real(REAL_KIND), parameter     :: DPHI = PI/180. 

    real(REAL_KIND), dimension(0:SAMPLE_LENGTH-1) :: arr 
    real(REAL_KIND)        :: r 
    integer          :: i 


    open(UNIT, file="sin.dat", form='unformatted',& 
       access='direct', recl=4) 

    do i = 0,ubound(arr, 1) 
     read(UNIT, rec=i+1, err=100) arr(i) 
    end do 

    do i = 0,ubound(arr, 1) 
     r = 1. - (arr(i)**2. + cos(real(i*DPHI, REAL_KIND))**2) 
     write(*, '(F6.4, " ")', advance='no')& 
      real(int(r*1E6+1)/1E6, REAL_KIND) 
    end do 

100 close(UNIT)  
    write(*,*) 
end program numpy_import 

also wenn val == sin(x), die numerische Resul t muss in guter Näherung für float32-Typen verschwinden.

Und in der Tat:

Ausgang:

360 x 0.0000 
1

So dank dieser großen Gemeinschaft, von der ganzen rate ich bekam, und ein wenig herum bastelt, ich glaube, ich herausgefunden ein stabile Lösung für dieses Problem, und ich wollte mit Ihnen all diese Antwort teilen. Ich werde hier ein minimales Beispiel geben, wo ich ein Array variabler Größe aus Python in eine Binärdatei schreiben und es mit Fortran lesen möchte. Ich gehe davon aus, dass die Anzahl der Zeilen numRows und die Anzahl der Spalten numCols auch zusammen mit dem vollständigen Array datatArray geschrieben werden. Die folgende Python-Skript writeBin.py schreibt die Datei:

import numpy as np 
# Read in the numRows and numCols value 
# Read in the array values 
numRowArr = np.array([numRows], dtype=np.float32) 
numColArr = np.array([numCols], dtype=np.float32) 
fileObj = open('pybin.bin', 'wb') 
numRowArr.tofile(fileObj) 
numColArr.tofile(fileObj) 
for i in range(numRows): 
    lineArr = dataArray[i,:] 
    lineArr.tofile(fileObj) 
fileObj.close() 

Im Anschluss daran der Fortran-Code, um das Array zu lesen aus der Datei wie folgt programmiert werden:

program readBin 

    use iso_fortran_env 

    implicit none 

    integer:: nR, nC, i 

    real(kind=real32):: numRowVal, numColVal 
    real(kind=real32), dimension(:), allocatable:: rowData 
    real(kind=real32), dimension(:,:), allocatable:: fullData 

    open(unit=10,file='pybin.bin',form='unformatted',status='old',access='stream') 

    read(unit=10) numRowVal 
    nR = int(numRowVal) 

    read(unit=10) numColVal 
    nC = int(numColVal) 

    allocate(rowData(nC)) 
    allocate(fullData(nR,nC)) 

    do i = 1, nR 

     read(unit=10) rowData 
     fullData(i,:) = rowData(:) 

    end do 

    close(unit=10) 

end program readBin 

Der wichtigste Punkt, den ich von dem geraffte Diskussion über diesen Thread ist so viel wie möglich zu lesen und schreiben, mit genauen Spezifikationen der Datentypen zu lesen, wie sie geschrieben werden usw. Wie Sie vielleicht bemerken, ist dies ein erfundenes Beispiel, so kann es seien Sie hier und da ein paar Dinge, die nicht perfekt sind. Allerdings habe ich dies jetzt benutzt, um ein Finite-Elemente-Programm zu programmieren, und die Mesh-Daten waren dort, wo ich dieses binäre Lesen/Schreiben verwendet habe - und es hat sehr gut funktioniert.

P.S: Falls Sie einen Tippfehler finden, lassen Sie es mich bitte wissen und ich werde es sofort bearbeiten.

Vielen Dank.