Reading a sequence of integer in a line with unknown bound in fortran

我只是一个虾纸丫 提交于 2019-12-10 23:34:41

问题


I would like to read a sequence of integer in a line with unknown bound in FORTRAN. My question is similar to the following previous post,

Reading a file of lists of integers in Fortran

however I want to read a sequence of unknown numbers of integer in a line and save it in separate arrays. And successive lines of integer should be saved to some other array

My file looks like this
5 7 8 9 10 13            # should be stored  f(1)(6) arrays
93 102 92                # c(1)(3)
105 107 110 145 147 112  # f(2)(6)
97 98                    # b(1)(2)
12 54 55                 # c(2)(3)
15 17 21 23 45           # e(1)(5)
43 47 48 51 62           # d(1)(4)

Thus I have a sequence of integers with maximum length of 6 (to be stored in f array) and minimum length of 2(to be stored in b array). I have hundreds of lines like this, such that I need to classify according to the maximum length and count on them.

Reading a file of lists of integers in Fortran


回答1:


There are probably many ways to do this, and the following is one such example. Here, the split() makes multiple trials for list-directed input for all values in the line, until non-numeric characters or the end of line is encountered.

subroutine split( line, vals, n )
    implicit none
    character(*), intent(in) :: line
    real*8  :: vals(*), buf( 10000 )
    integer :: n

    n = 1
    do
        read( line, *, end=100, err=100 ) buf( 1 : n )   !! (See Appendix for why buf is used here)
        val( 1:n ) = buf( 1:n )
        n = n + 1
    enddo
100 continue
    n = n - 1
end

program main
    implicit none
    character(200) :: line
    real*8  :: vals( 10000 )
    integer :: n

    open( 10, file="test.dat", status="old" )
    do
        read( 10, "(a)", end=500 ) line
        call split( line, vals, n )

        if ( n == 0 ) then
            print *, "comment line"
        else
            print *, nint( vals( 1 : n ) )
        endif
    enddo
500 continue
    close( 10 )
end

If test.dat contains the whole lines in the Question, plus the following lines

# additional data
1,2,3 , 4 , 5            # comma-separated integers
1.23e2  -4.56e2 , 777    # integer/floating-point mixed case

it gives

comment line
5 7 8 9 10 13
93 102 92
105 107 110 145 147 112
97 98
12 54 55
15 17 21 23 45
43 47 48 51 62
comment line
1 2 3 4 5
123 -456 777

So one can save the result for each line by copying the values in vals(1:n) to a desired array.

[ Appendix (thanks to @francescalus) ] In the above code, the data are read once into buf(1:n) and then copied to val(1:n). One might think that it would be more direct to read in the data into val(1:n) such that

read( line, *, end=100, err=100 ) val( 1 : n )

However, this direct approach is not recommended because val(1:n) becomes undefined when the read statement hits the "end" or "err" condition. Although ifort and gfortran seem to retain the data in val(1:n) even when that condition is met (and so they work even with the direct approach), the same behavior cannot be guaranteed for other compilers. In contrast, the buffer approach avoids this risk by saving the data one step before to val(1:n), so that undefined data are not used. This is why the buffer approach is used in the above code despite it is one statement longer.




回答2:


Something like this might satisfy your requirements

  INTEGER :: ix, rdstat
  INTEGER, DIMENSION(6) :: nums
  CHARACTER(len=128) :: aline
  ...
  OPEN(21,file='data.txt')

  DO ix = 1,10
     READ(21,'(a)',iostat=rdstat) aline
     IF (rdstat/=0) catch_errors()

     nums = -99 ! a guard
     READ(aline,*,iostat=rdstat) nums
     IF (rdstat/=0) catch_errors()
     ! do something with nums
  END DO

  CLOSE(21)

I haven't thoroughly tested this and I haven't written catch_errors for you -- in practice you may not want to do very much. This first version is probably too brittle, but whether it's suitable or not depends heavily on the uniformity (or otherwise) of the input files.

The strategy is to read each line into a character variable (one long enough for the entire line) and then to use internal, list-directed, reading to read 6 integers from the start of the character variable. This takes advantage of the in-built facility that list-directed input has of finding integers in an input stream with spaces separating values. The approach should work as well with a comma-separated list of integers. The internal read only looks for 6 integers, then catches the error when either no more integers are found, or only material which cannot be interpreted as integers (such as strings like # comment).

Note

  • I've assumed a maximum line length of 128 characters, you may want to adjust that.
  • I've specified a fixed upper limit on the number of lines the program will read. You may want to change that, or change to a do/while loop.
  • The program expects no more than 6 integers in a line, as your question specifies.
  • At each line read the array nums is filled with -99 at each element; this is a 'guard'. If -99 is likely to occur in your input files you may want to change this.
  • It's entirely up to you what you do with the numbers in nums once you have them.


来源:https://stackoverflow.com/questions/32022062/reading-a-sequence-of-integer-in-a-line-with-unknown-bound-in-fortran

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!