Read binary file containing strings and numbers in Fortran

55 Views Asked by At

I am trying to read a binary file in Fortran. The file contains strings and numbers. I know how the binary file was created, since I have the code. Here is the minimal reproducible example of code that creates such a binary file:

PROGRAM MAIN
    
    USE, INTRINSIC :: ISO_FORTRAN_ENV
    
    IMPLICIT NONE
    
    INTEGER, PARAMETER :: I32 = INT32
    INTEGER, PARAMETER :: R32 = REAL32
    CHARACTER(1_I32) , PARAMETER :: END_REC = CHAR(10_I32) !end-character for binary-record finalize
    CHARACTER(*), PARAMETER :: MAIN_FILE_NAME = "main_file"
    INTEGER(KIND=I32) :: E_IO, I, UNIT_MAIN, UNIT_SCRATCH
    INTEGER(KIND=I32) :: N_BYTE !number of byte to be written/read
    INTEGER(KIND=I32) :: IOFFSET, IOFFSET_1, IOFFSET_2
    INTEGER(KIND=I32) , PARAMETER :: MAXLEN = 500_I32
    CHARACTER(LEN=MAXLEN)    :: S_BUFFER
    REAL(KIND=R32) :: RPRO
    INTEGER(KIND=I32) :: IPRO
    INTEGER(KIND=I32), PARAMETER :: N_INT_VAL = 2, N_REAL_VAL = 3
    REAL(KIND=R32), DIMENSION(N_REAL_VAL) :: X, Y, Z
    INTEGER(KIND=I32), DIMENSION(N_INT_VAL) :: INT_ARRAY
    CHARACTER(2) :: VAR_TYPE
    INTEGER(KIND=I32) :: N_V
    REAL(KIND=R32), ALLOCATABLE :: V_R4(:)
    REAL(KIND=I32), ALLOCATABLE :: V_I4(:)
    !initlialization of data arrays
    X = (/0.0_R32, 1.0_R32, 3.0_R32/)
    Y = (/0.0_R32, 2.0_R32, 1.0_R32/)
    Z = (/0.0_R32, 4.0_R32, 3.0_R32/)
    INT_ARRAY = (/1_I32, 100_I32/)
    
    UNIT_MAIN = 11
    UNIT_SCRATCH = 12
    
    !open the binary file
    OPEN(UNIT=UNIT_MAIN, FILE=MAIN_FILE_NAME,                         &
         FORM='UNFORMATTED', ACCESS='STREAM',             &
         ACTION='WRITE', CONVERT='BIG_ENDIAN', IOSTAT=E_IO)
    
    !open SCRATCH file for appending raw binary data
    OPEN(UNIT=UNIT_SCRATCH,                                                               &
         FORM='UNFORMATTED', ACCESS='STREAM',                                   &
         ACTION='READWRITE', CONVERT='BIG_ENDIAN', STATUS='SCRATCH', IOSTAT=E_IO)
    IOFFSET = 0_I32 ! initializing offset
    
    WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO)'<version="1.0"? byte_order="BigEndian>'//END_REC
    
    !------prepare real points X, Y, Z------
    WRITE(S_BUFFER, FMT='(A,'//'(I12)'//',A)', IOSTAT=E_IO)'<NumberOfRealPoints="',N_REAL_VAL,'">'
    WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO)TRIM(S_BUFFER)//END_REC
    
    WRITE(S_BUFFER,FMT='(I8)', IOSTAT=E_IO)IOFFSET
    WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO)'<DataArray type="Float32" NumberOfComponents="3" offset="',TRIM(S_BUFFER),'">'//END_REC
    
    N_BYTE  = 3_I32*N_REAL_VAL*SIZEOF(RPRO)
    IOFFSET = IOFFSET + sizeof(IPRO) + N_BYTE  !0 + size(N_BYTE) + size(X+Y+Z) = 40 bytes
    IOFFSET_1 = IOFFSET
    WRITE(UNIT=UNIT_SCRATCH, IOSTAT=E_IO) N_BYTE,'R4',3_I32*N_REAL_VAL
    WRITE(UNIT=UNIT_SCRATCH, IOSTAT=E_IO)(X(I),Y(I),Z(I),   I=1_I32, N_REAL_VAL) !write real points to SCRATCH file
    !--------------------------------------------------
    !------prepare integer array INT_ARRAY------
    WRITE(S_BUFFER, FMT='(A,'//'(I12)'//',A)', IOSTAT=E_IO)'<NumberOfIntValues="',N_INT_VAL,'">'
    WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO)TRIM(S_BUFFER)//END_REC
    
    WRITE(S_BUFFER,FMT='(I8)', IOSTAT=E_IO)IOFFSET
    WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO)'<DataArray type="Int32" NumberOfComponents="1" offset="',TRIM(S_BUFFER),'">'//END_REC
    
    N_BYTE  = 1_I32*N_INT_VAL*SIZEOF(IPRO)
    IOFFSET = IOFFSET + sizeof(IPRO) + N_BYTE  !size(N_BYTE) + size(X+Y+Z) + size(N_BYTE) + size(INT_ARRAY)  = 40 bytes + 12 bytes = 52 bytes
    IOFFSET_2 = IOFFSET
    WRITE(UNIT=UNIT_SCRATCH, IOSTAT=E_IO) N_BYTE,'I4',1_I32*N_INT_VAL
    WRITE(UNIT=UNIT_SCRATCH, IOSTAT=E_IO)(INT_ARRAY(I),   I=1_I32, N_INT_VAL)
    !--------------------------------------------------
    WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO)'<end text>'//END_REC
    WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO)'_'
    
    
    ENDFILE(UNIT=UNIT_SCRATCH, IOSTAT=E_IO)
    REWIND(UNIT=UNIT_SCRATCH, IOSTAT=E_IO)
    DO
      READ(UNIT=UNIT_SCRATCH, IOSTAT=E_IO, END=100) N_BYTE, VAR_TYPE, N_V
      SELECT CASE(VAR_TYPE)
          CASE('R4')
            ALLOCATE(V_R4(1_I32:N_V))
            READ (UNIT=UNIT_SCRATCH, IOSTAT=E_IO)(V_R4(I),I=1_I32,N_V)
            WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO) N_BYTE, (V_R4(I),I=1_I32,N_V)
            DEALLOCATE(V_R4)
          
          CASE('I4')
              ALLOCATE(V_I4(1_I32:N_V))
              READ (UNIT=UNIT_SCRATCH, IOSTAT=E_IO)(V_I4(I),I=1_I32,N_V)
              WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO) N_BYTE, (V_I4(I),I=1_I32,N_V)
              DEALLOCATE(V_I4)
      END SELECT
        
    END DO
    
    100 CONTINUE
    WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO)END_REC
    WRITE(UNIT=UNIT_MAIN, IOSTAT=E_IO)'<end file>'//END_REC
    
    CLOSE(UNIT=UNIT_SCRATCH, IOSTAT=E_IO)
    CLOSE(UNIT=UNIT_MAIN, IOSTAT=E_IO)
    
    CALL READ_FILE(MAIN_FILE_NAME,N_INT_VAL,N_REAL_VAL,IOFFSET_1,IOFFSET_2)
    
END PROGRAM MAIN

The code writes into file text header (7 strings) and then the arrays X, Y, Z, INT_ARRAY and the footer at the end. I am able to read the text (7 string) using READ_FILE subroutine:

SUBROUTINE READ_FILE(MAIN_FILE_NAME,N_INT_VAL,N_REAL_VAL,IOFFSET_1,IOFFSET_2)
    
    USE, INTRINSIC :: ISO_FORTRAN_ENV
    
    IMPLICIT NONE
    
    INTEGER, PARAMETER :: I32 = INT32
    INTEGER, PARAMETER :: R32 = REAL32
    INTEGER(KIND=I32) :: E_IO, I, N_V
    CHARACTER(*), INTENT(IN) :: MAIN_FILE_NAME
    INTEGER(KIND=I32), INTENT(IN) :: N_INT_VAL, N_REAL_VAL
    INTEGER(KIND=I32), INTENT(IN) :: IOFFSET_1, IOFFSET_2
    CHARACTER(LEN=100) :: DUMMY
    CHARACTER(LEN=1) :: ONECH
    INTEGER(KIND=I32) :: N_BYTE
    REAL(KIND=R32), DIMENSION(:), ALLOCATABLE :: REAL_ARRAY_READ
    INTEGER(KIND=I32), DIMENSION(:), ALLOCATABLE :: INT_ARRAY_READ
    
    ALLOCATE(REAL_ARRAY_READ(3_I32*N_REAL_VAL), INT_ARRAY_READ(N_INT_VAL))
    
    
    OPEN(UNIT=11,FILE=TRIM(MAIN_FILE_NAME),STATUS='old',iostat=E_IO)
    READ(UNIT=11,FMT='(A)') DUMMY !'<version="1.0"? byte_order="BigEndian>'
    READ(UNIT=11,FMT='(A)') DUMMY !'<NumberOfRealPoints="3">'
    READ(UNIT=11,FMT='(A)') DUMMY !'<DataArray type="Float32" NumberOfComponents="3" offset="0">'
    READ(UNIT=11,FMT='(A)') DUMMY !'<NumberOfIntValues="2">'
    READ(UNIT=11,FMT='(A)') DUMMY !'<DataArray type="Int32" NumberOfComponents="1" offset="40">'
    READ(UNIT=11,FMT='(A)') DUMMY !'<end text>'
    READ(UNIT=11,FMT='(A)') ONECH !'_'
    N_V = 3_I32*N_REAL_VAL
    !READ(UNIT=11) N_BYTE, (REAL_ARRAY_READ(I),I=1_I32,N_V)
    
    CLOSE(11)
    
    
END SUBROUTINE READ_FILE

However, I am not sure how to extract the numerical values of arrays from this binary file. Do I need to reopen the file as UNFORMATTED and somehow position reading after the text? Could you please help with correctly reading the values of X, Y, Z, INT_ARRAY (REAL_ARRAY_READ, INT_ARRAY_READ respectively)?

0

There are 0 best solutions below