Community
cancel
Showing results for 
Search instead for 
Did you mean: 
WSinc
New Contributor I
1,003 Views

fortran binary files

I am under the impression I can make a binary file that allows random access to a set of fixed size records.

Its allowing me to write records sequentially, but if I pick a record number, I cant do that.

 

for example if I have 100 records of 20 integer words each, I should be able to pick and replace record number 48, right?

 

Maybe I dont have the right set of keywords to open the file. But you dont give us any examples that I can find.

0 Kudos
39 Replies
WSinc
New Contributor I
677 Views

subroutine screwup
integer(4) pr(6000)
open(2,file='screwup2.dat',form='binary',recl=6000)
do 10 irec=1,100
10 write(2,rec=irec)pr
rewind 2
do 20 irec=1,100
read(2,rec=irec)pr
20 print *,"rec ",irec," read"
pause
end

 

As long as I write the files and read them sequentially, it works.

But I cannot pick a record randomly. So that rec=irec option illegal ?

Paul_Curtis
Valued Contributor I
512 Views

If your application is able to forego portability to any opsys but Windows, then you can easily replace the standard (antiquated) Fortran file i/o routines with WinAPI routines.  Concepts such as "records", as well as even more obsolete notions (BACKSPACE, REWIND) no longer constrain the i/o functions.

The WinAPI routines allow you to specify an exact byte count to be transferred to/from any memory location, from/to any position in the file at a specified byte-count offset.  That's about as atomic and powerful as it gets, and there is nothing, including all the OP asked for, that cannot be easily and directly accomplished.

The Fortran routines below (extracted from a module in a much larger program) illustrate how this is done.  Note that file and path namestrings need to be null-terminated for Windows.

Here is a sample calling sequence to load a program variable with nbytes of data read starting at offset_value in the file:

ihandl = Open_the_file ('c:\mydir\fname'c, 'R')

CALL rw_file ('R', ihandl, nbytes, LOC(mydata), offset_value)

CALL close_file (ihandl)

 

Simple, powerful, and also much much faster than using the built-in Fortran file i/o.

 

MODULE filesubs
    
    USE ifwin
    USE ifwinty

	SAVE


CONTAINS

	RECURSIVE FUNCTION file_exists (fname) RESULT (size)
		IMPLICIT NONE
		TYPE(T_WIN32_FIND_DATA)			:: fdata
		CHARACTER(LEN=*),INTENT(IN) 	:: fname  ! full pathname to file
		INTEGER(HANDLE)                 :: ihandl
		INTEGER							:: size, rval, nc
		size = -1
		nc = chcnt(fname, LEN(fname))
		IF (nc > 5) THEN
			ihandl = FindFirstFile (fname, fdata)
			IF (ihandl /= INVALID_HANDLE_VALUE) THEN
				size = fdata%nFileSizeLow

				! must have this, else memory leak
				rval = FindClose (ihandl)
			ELSE
			    ! error
			ENDIF
		END IF
	END FUNCTION file_exists


	RECURSIVE FUNCTION open_the_file (fullpath, rwmode) RESULT (ihandl)
		IMPLICIT NONE
		SAVE
		INTEGER(HANDLE)                 :: ihandl
		INTEGER							:: access, nc
		INTEGER                         :: ntry
		INTEGER, PARAMETER              :: max_try = 10
		CHARACTER(LEN=*),INTENT(IN)		:: fullpath, rwmode
		CHARACTER(LEN=200)				:: msg
 		
		IF (rwmode == 'R') THEN
			access = GENERIC_READ
		ELSE
			access = IOR(GENERIC_READ,GENERIC_WRITE)
		END IF		

        ntry = 0
	10  ihandl = CreateFile (fullpath,					&       ! null-terminated
							 access,					&
							 IOR(FILE_SHARE_READ,FILE_SHARE_WRITE),	&
							 NULL_SECURITY_ATTRIBUTES,	& 
							 OPEN_ALWAYS,				&
							 FILE_ATTRIBUTE_NORMAL,		&
							 NULL						)

		IF (ihandl == INVALID_HANDLE_VALUE) THEN
		    IF (GetLastError() == ERROR_SHARING_VIOLATION) THEN
		        ntry = ntry + 1
		        IF (ntry > max_try) RETURN
		        CALL delay_ms (5)
		        GO TO 10
		    END IF
		END IF
		
	END FUNCTION open_the_file


!     TYPE T_WIN32_FIND_DATA
!     SEQUENCE
!       integer(DWORD)		dwFileAttributes	! knowns    DWORD 
!       TYPE (T_FILETIME)	ftCreationTime		! typedefs  FILETIME 
!       TYPE (T_FILETIME)	ftLastAccessTime	! typedefs  FILETIME 
!       TYPE (T_FILETIME)	ftLastWriteTime		! typedefs  FILETIME 
!       integer(DWORD)		nFileSizeHigh		! knowns    DWORD 
!       integer(DWORD)		nFileSizeLow		! knowns    DWORD 
!       integer(DWORD)		dwReserved0			! knowns    DWORD 
!       integer(DWORD)		dwReserved1			! knowns    DWORD 
!       character(260)		cFileName
!       character(14)		cAlternateFileName
!    END TYPE


	RECURSIVE SUBROUTINE rw_file (rwmode, ihandl, nbytes, loc_pointer, offset)
		IMPLICIT NONE
		SAVE
		CHARACTER(LEN=1), INTENT(IN)	:: rwmode
		INTEGER(HANDLE), INTENT(IN)     :: ihandl
		INTEGER, INTENT(IN)				:: nbytes, loc_pointer
		INTEGER, INTENT(IN), OPTIONAL	:: offset
		INTEGER                         :: rval
		INTEGER     					:: nact

		! position pointer if offset is provided
    	IF (PRESENT(offset)) rval = SetFilePointer (ihandl, offset, NULL, FILE_BEGIN)

		IF (rwmode == 'R') THEN
			IF (ReadFile (ihandl,			&  ! file handle
				     	  loc_pointer,		&  ! address of data
						  nbytes,			&  ! byte count to read
						  LOC(nact),		&  ! actual bytes read
						  NULL_OVERLAPPED) == 0) THEN
			  	! Error reading file
			END IF
		
		ELSE
			IF (WriteFile (ihandl,			&  ! file handle
						   loc_pointer,		&  ! address of data
						   nbytes,			&  ! byte count to write
						   LOC(nact),		&  ! actual bytes written
						   NULL_OVERLAPPED) == 0) THEN
			  	! Error writing file
			END IF
		END IF
				
	END SUBROUTINE rw_file
    
	
	RECURSIVE SUBROUTINE Set_File_Pointer (ihandl, offset, truncate)
		IMPLICIT NONE
		INTEGER(HANDLE), INTENT(IN)     :: ihandl
		INTEGER, INTENT(IN)				:: offset
		LOGICAL, INTENT(IN), OPTIONAL	:: truncate
		INTEGER							:: rslt
		rslt = SetFilePointer (ihandl, MAX0(offset,0), NULL, FILE_BEGIN)
		
		IF (PRESENT(truncate)) rslt = SetEndOfFile (ihandl)				
	END SUBROUTINE Set_File_Pointer


	RECURSIVE SUBROUTINE close_file (ihandl)
		IMPLICIT NONE
		INTEGER(HANDLE), INTENT(INOUT)  :: ihandl
		LOGICAL					        :: rslt
		!IF (ihandl > 0) rslt = CloseHandle (ihandl)
		rslt = CloseHandle (ihandl)
		ihandl = 0
		filehandles = MAX0(0, filehandles - 1)
	END SUBROUTINE close_file

	
END MODULE filesubs

 

mecej4
Black Belt
667 Views

Read about the ACCESS='DIRECT' specifier in the file OPEN statement. Related to this is he specifier FORM='...'

Do not use the nonstandard FORM='BINARY' unless you understand the implications.

WSinc
New Contributor I
656 Views

I have to say Form="binary" since I am working with Midi files,

generated by music software.

 

Anyway thanks for the helpful tips.

Steve_Lionel
Black Belt Retired Employee
640 Views

form='binary' is an extension. Instead you should be using form="unformatted", access="stream".

WSinc
New Contributor I
630 Views

Well, I still cant get it to access that file, when I use your suggestion.

It says "file not found" even when the file is where it can easily find it.

So should I be able to attach the file, regardless of what kind it is ?

subroutine getmid()
character*20 FN/"LAFA.mid"/
integer(4) midirec(60)
!
110  format(A20)
12    open(2,file="LAFA.MID", &
form="unformatted", access="stream", action="READ")
do irec=1,100
    read(2)midirec
    print *,"record ",irec," read"
enddo
end

 

Didnt someone write about this matter recently ?

jimdempseyatthecove
Black Belt
626 Views

>>It says "file not found" even when the file is where it can easily find it.

Then most likely the (your) current directory is not where you expect it to be.

The current directory can be located anywhere.

When launched from an IDE, the current directory is usually specified on some property page (e.g. Debug or Release configuration). Default for MS Visual Studio is the Project Folder, but you can set it anywhere else. Your executable is likely located elsewhere.

jimdempseyatthecove_0-1594241470589.png

 

You could add an OPEN file for output such as "WhereAmI" write something to it (e.g. "Here I am"), close the file. Then find the file.

There is also a Current Drive.

Using "LAFA.MID" specifies the file is located in the "current directory" on the "current drive.

Function GETDRIVEQQ and GETDRIVEDIRQQ can also be used to query the system.

Jim Dempsey

Jim Dempsey

WSinc
New Contributor I
579 Views

I made sure the executable is where where Its supposed to be,

By opening a scratch file and seeing where it gets put. I can verify that it is always put in the same location as the data I want to look at.

And I can always attach the file on an OPEN statement, but every time I try to read one record it gives me an EOF return, rather than reading in the data.

 

Is there someone at INTEL that has actually had to deal with a similar problem, so we dont do this endless guesswork ? I will pay for their time.

The file I am working with was probably generated on a MAC machine, which is why I have to treat it as a "strange" format, with a raw set of data bytes.

andrew_4619
Honored Contributor I
561 Views

Can you share the file you are trying to read?

WSinc
New Contributor I
544 Views

It doesn't give give me a way to do that.

Not obvious, anyway. It is sitting in my user file directory,

but how do I transfer the whole file ?

 

This was imported from outside, but I find it hard to understand why there is no way to examine the contents of it, even as a "raw binary"

 

Would access="STREAM" be the correct way ?

WSinc
New Contributor I
545 Views

Here is the file I am trying to read.

 

I am trying to use "drag and drop." or Browse files.

 

But nothing happens when I click on that.

 

Dont they bother to check this stuff ?

Steve_Lionel
Black Belt Retired Employee
542 Views

You can put the file in a ZIP archive and attach that. I am going to test that here. (It worked. Be sure to drag it onto the "attach" box and not the message box.)

Would you please show the actual and complete error message you get for "file not found" and also a directory listing of the directory you think the file should be found in? That you sometimes get EOF tells me that it is being created empty because the intended file is not there. 

I do recommend using ACCESS='STREAM', FORM='UNFORMATTED' (and unformatted READs) to read binary data.

WSinc
New Contributor I
534 Views

input file name:
lafa.mid
Access method:
stream
LGU 2 file opened OK
forrtl: severe (24): end-of-file during read, unit 2, file C:\Users\Administrator\documents\visual studio 2010\Projects\Console8\Console8\lafa.mid
Image PC Routine Line Source
libifcoremdd.dll 510B19D2 Unknown Unknown Unknown
libifcoremdd.dll 510EED7F Unknown Unknown Unknown
Console8.exe 00C612A6 _GETMIDI 29 CONSOLE8.F90
Console8.exe 00C63F37 _MAIN__ 7 CONSOLE8.F90
Console8.exe 00C65CAF Unknown Unknown Unknown
Console8.exe 00C6466F Unknown Unknown Unknown
Console8.exe 00C6449F Unknown Unknown Unknown
KERNEL32.DLL 76B16359 Unknown Unknown Unknown
ntdll.dll 77467B74 Unknown Unknown Unknown
ntdll.dll 77467B44 Unknown Unknown Unknown

 

You can see that it attaches the file, but I get the ERROR QUIT when it tries to do any reading from it. I did use the Stream input method, as you recommended.

Steve_Lionel
Black Belt Retired Employee
531 Views

We'd need to see a small test program that demonstrates the problem. It could be that your program is reading past the end of the file. Please attach ZIP of the file and a program we can look at. Ideally this would be a small program that just tries to read some data from the file, but if you can't make that work the whole program is ok.

WSinc
New Contributor I
530 Views

OK, here is the source code. I am trying to read a standard MIDI file from a music program.

 

subroutine getmidi()
character*20 FN 
character*20 AM
integer(4) midirec(60)
!
110 format(A20)
12 print *,"input file name:"
READ(*,110)FN
print *,"Access method:"
READ(*,110)AM
open(2,file=FN,access=AM, form="UNFORMATTED")
35 print *,"LGU 2 file opened OK"
do irec=1,100
! this is where it falls apart
! It gives me an EOF, rather than reading in any data.
! I am trying to use STREAM input
read(2)midirec
print *,"record ",irec," read"
print *,"contents:",midirec
enddo
end

WSinc
New Contributor I
529 Views

BTW, the file I was reading has more than 230K bytes of data, so I dont think I would be getting an End-of-File right at the beginning of the 1st read operation. I had trouble attaching the actual file, but I can make a more concerted effort if needed.

 

It keeps telling me the file type is "not supported." Its a standard file used every where, so I dont see why.

 

You would probably get the same problem, no matter what type it is ?

WSinc
New Contributor I
588 Views

Well, this MIDI file is just one example of the problem, namely reading in files where you want to examine their contents, but you dont know their origins ahead of time.

Of course, once you DO know that, then there might be a better way to manipulate their contents. But the first step is to find out WHAT they ARE first.

 

The problem is, if it gives you an EOF ERROR without any info, then you have learned nothing whatsoever.

 

I was wondering is: INTEL if had a tool where you can examine a file byte by byte without knowing its format ahead of time.

WSinc
New Contributor I
586 Views

The NEXUS link does not go anywhere, BTW.

mecej4
Black Belt
582 Views

Sorry, there should have been a space between the link and the comma after it. That is, the correct link is 

https://mh-nexus.de/en/hxd/

instead of

https://mh-nexus.de/en/hxd/,

I tried to fix my previous post, which you read, by adding a space between the URL and the comma, and this *@~%! forum software marked the post as spam and deleted it! Just because I added a space!

mecej4
Black Belt
576 Views

WSinc, you wrote: "Of course, once you DO know that, then there might be a better way to manipulate their contents. But the first step is to find out WHAT they ARE first."

Sorry, in general inferring the structure of a file format from a small number of example files is extremely difficult and rarely worth the effort. Reverse engineers and hackers have special skills and tools for that, and they would probably not use Fortran.

Most people, after looking at a binary file without knowing something about its contents, would think that they are seeing random bytes. Try looking at EXE or Zip files using HXD.

 

Reply