开发者

Read numbers following a keyword into an array in Fortran 90 from a text file

I have many text files of this format

....
<snip>
'FOP' 0.19 1 24 1 25 7 8  /
'FOP' 0.18 1 24 1 25 9 11 /
/ 

TURX
560231
300244
70029
200250
645257
800191
900333
600334
770291
300335
220287
110262 /

SUBTRACT
'TURX' 'TURY'/
</snip>
......

where the portions I snipped off contain other various data in various formats. The file format is inconsistent (machine generated), the only thing one is assured of is the keyword TURX which may appear more than once. If it appears alone on one line, then the next few lines will contain numbers that I need to fetch into an array. The last number will have a space then a forward slash (/). I can then use this array in other operations afterwards.

How do I "search" or parse a file of unknown format in fortran, and how do I get a loop to fetch the rest of the data, please? I am really new to this and I HA开发者_如何学运维VE to use fortran. Thanks.


Fortran 95 / 2003 have a lot of string and file handling features that make this easier.

For example, this code fragment to process a file of unknown length:

   use iso_fortran_env


  character (len=100) :: line
   integer :: ReadCode


ReadLoop: do
  read (75, '(A)', iostat=ReadCode )  line

  if ( ReadCode /= 0 ) then
     if ( ReadCode == iostat_end ) then
        exit ReadLoop
     else
        write ( *, '( / "Error reading file: ", I0 )' )  ReadCode
        stop
     end if
  end if

  ! code to process the line ....

end do ReadLoop

Then the "process the line" code can contain several sections depending on a logical variable "Have_TURX". If Have_TRUX is false you are "seeking" ... test whether the line contains "TURX". You could use a plain "==" if TURX is always at the start of the string, or for more generality you could use the intrinsic function "index" to test whether the string "line" contains TURX.

Once the program is in the mode Have_TRUX is true, then you use "internal I/O" to read the numeric value from the string. Since the integers have varying lengths and are left-justified, the easiest way is to use "list-directed I/O": combining these:

read (line, *) integer_variable

Then you could use the intrinsic function "index" again to test whether the string also contains a slash, in which case you change Have_TRUX to false and end reading mode.

If you need to put the numbers into an array, it might be necessary to read the file twice, or to backspace the file, because you will have to allocate the array, and you can't do that until you know the size of the array. Or you could pop the numbers into a linked list, then when you hit the slash allocate the array and fill it from the linked list. Or if there is a known maximum number of values you could use a temporary array, then transfer the numbers to an allocatable output array. This is assuming that you want the output argument of the subroutine be an allocatable array of the correct length, and the it returns one group of numbers per call:

integer, dimension (:), allocatable, intent (out) :: numbers
allocate (numbers (1: HowMany) )

P.S. There is a brief summary of the language features at http://en.wikipedia.org/wiki/Fortran_95_language_features and the gfortran manual has a summary of the intrinsic procedures, from which you can see what built in functions are available for string handling.


I'll give you a nudge in the right direction so that you can finish your project.

Some basics:

  • Do/While as you'll need some sort of loop structure to loop through the file and then over the numbers. There's no for loop in Fortran, so use this type.

  • Read to read the strings.

To start you need something like this:

  program readlines
  implicit none
  character (len=30) :: rdline
  integer,dimension(1000) :: array
  !  This sets up a character array with 30 positions and an integer array with 1000
  !
  open(18,file='fileread.txt')
  do
     read(18,*) rdline
     if (trim(rdline).eq.'TURX') exit  !loop until the trimmed off portion matches TURX
  end do

See this thread for way to turn your strings into integers.

Final edit: Looks like MSB has got most of what I just found out. The iostat argument of the read is the key to it. See this site for a sample program.


Here was my final way around it.

PROGRAM fetchnumbers
    implicit none
    character (len=50) ::line, numdata
    logical ::is_numeric        
    integer ::I,iost,iost2,counter=0,number
    integer, parameter :: long = selected_int_kind(10)
    integer, dimension(1000)::numbers !Can the number of numbers be up to 1000?

    open(20,file='inputfile.txt') !assuming file is in the same location as program
    ReadLoop: do
        read(20,*,iostat=iost) line !read data line by line
        if (iost .LT. 0) exit !end of file reached before TURX was found
        if (len_trim(line)==0) cycle ReadLoop !ignore empty lines
        if (index(line, 'TURX').EQ.1) then !prepare to begin capturing
            GetNumbers: do
                read(20, *,iostat=iost2)numdata !read in the numbers one by one
                if (.NOT.is_numeric(numdata)) exit !no more numbers to read             
                if (iost2 .LT. 0) exit !end of file reached while fetching numbers
                read (numdata,*) number !read string value into a number
                counter = counter + 1
                Storeloop: do I =1,counter
                    if (I<counter) cycle StoreLoop
                    numbers(counter)=number !storing data into array
                end do StoreLoop
            end do GetNumbers
        end if
    end do ReadLoop

    write(*,*) "Numbers are:"
    do I=1,counter
      write(*,'(I14)') numbers(I)
    end do

END PROGRAM fetchnumbers

FUNCTION is_numeric(string)
  IMPLICIT NONE
  CHARACTER(len=*), INTENT(IN) :: string
  LOGICAL :: is_numeric
  REAL :: x
  INTEGER :: e
  is_numeric = .FALSE.
  READ(string,*,IOSTAT=e) x
  IF (e == 0) is_numeric = .TRUE.
END FUNCTION is_numeric
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜