how to read from a nonformatted file in fortran

Discussion in 'Mac Programming' started by ssmmgg, Dec 26, 2009.

  1. ssmmgg macrumors newbie

    Joined:
    Dec 26, 2009
    #1
    hi,
    i have a file that contains lots of numbers like this:

    1 2 34 998 3 1 4
    2 4
    3
    6
    77789 908
    ...

    so we dont know how many numbers in line and even how many lines

    i want to read numbers from this file one by one and find how many even and odd numbers exist.
    what's the best?
    tanx a lot
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    I would just set open this as text, and make reads into a long character(*). you can define long based on your needs, 32768 would probably be the longest you'd want to deal with, but that's up to you. Read into that using '(a)' for your format. I would then use the index primitive to find the whitespace (hopefully it's well-known in advance, such as a single space. Then you can read from your character(*) using i5, i13, etc. Whatever is appropriate using array slicing, i.e.:
    read(input,'(i13)') tmp_int
    if you need these in an arbitrary length array, you can make an allocatable array. I always make a subroutine that takes an allocatable array, a single element to add, and the current position. You can make it internal and just take the element to add. Then as needed you can resize the array using a second allocatable array that's local to the subroutine, size it to the current size, copy the elements, deallocate the original, size it to a new bigger size, copy back, etc.

    -Lee
     
  3. ssmmgg thread starter macrumors newbie

    Joined:
    Dec 26, 2009
    #3
    show me an example

    tanx Lee
    could u show me an example of code that do this

    without an allocable array. :)
     
  4. crackpip macrumors regular

    Joined:
    Jul 23, 2002
    #4
    Why don't you use a shell script with the 'wc' command to either count the number of words or the number of lines in the file? Then pass the count as an argument to the Fortran program. If you are using an older Fortran standard and can't pass arguments, you could always just save the results to a temporary file and read it in the Fortran code.

    crackpip
     
  5. ssmmgg thread starter macrumors newbie

    Joined:
    Dec 26, 2009
    #5
    i can't

    i forced to do it with read command
    nothing else allowed:(
     
  6. crackpip macrumors regular

    Joined:
    Jul 23, 2002
    #6
    This sounds like some sort of homework assignment, so I'll give you few hints. You could use a do (or do while) loop. Inside the loop use non-advancing input to read each variable in the record, then use the iostat or eor specifier to check if you reached the end of the record. Once at the end of a record, Fortran should automatically advance to the next record. You can also use the end argument to handle reaching the end of the file or again error handling with iostat. A negative iostat means end of record or end of file, while positive means a formatting error.

    Lee's solution will be much quicker because it's not making so many discrete file operations.

    Good luck,
    crackpip
     
  7. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #7
    Code:
    program evenodd
      implicit none
      character(32767) :: inp
      integer(4) :: tmp,oddCnt = 0,evenCnt = 0
      integer(4), allocatable :: allVals(:)
      integer(2) :: st,numVals = 0
      logical :: keepReading
    
      open(unit=9,file='./myInput.txt')
      keepReading = .true.
      do while(keepReading)
        read(9,'(a)',iostat=st) inp
        if(st < 0) then
          keepReading = .false.
        else
          call countLine()
        endif
      enddo
      oddCnt = count(btest(allVals(1:numVals),0))
      evenCnt = numVals - oddCnt
      write(6,'("Num even: ",i5," Num odd: ",i5)') evenCnt,oddCnt
    
      contains
        subroutine countLine()
          implicit none
          integer(2) :: stpos = 1,endpos
          inp=trim(adjustl(inp))
          if(len_trim(inp) == 0) return
          do while(.true.) 
            if(index(inp(1:len_trim(inp)),' ') > 0) then
              endpos=index(inp,' ')
            else
              endpos=len_trim(inp)
            endif
            read(inp(stpos:endpos),'(i13)',iostat=st) tmp
            if(st == 0) call addVal(tmp)
            if(endpos == len_trim(inp)) exit
            inp=trim(adjustl(inp(endpos+1:)))
          enddo
        end subroutine countLine
    
        subroutine addVal(x)
          implicit none
          integer(4), intent(in) :: x
          integer(4), allocatable :: tmpStore(:)
          if(.not. allocated(allVals)) allocate(allVals(8))
          if(size(allVals) < numVals + 1) then
            if(allocated(tmpStore)) deallocate(tmpStore)
            allocate(tmpStore(size(allVals)))
            tmpStore=allVals
            deallocate(allVals)
            allocate(allVals(size(tmpStore)*4))
            allVals=tmpStore
            deallocate(tmpStore)
          endif
          numVals = numVals + 1
          allVals(numVals) = x
        end subroutine addVal
      
    end program evenodd
    
    This isn't the cleanest... but it's not for work, etc. so i'm not that concerned.

    I did it with an allocatable array, even though for this purpose there is no reason to keep all values in memory, you can do the test an item at a time. I did it this way because you asked me not to, and this sounds like an assignment. Now if you try to turn this in unaltered, you won't be able to explain the (presumably uncovered) use of allocatables, etc. I have a very similar version that just does the count an item at a time, but don't want to post it in a manner that you could just turn it in.

    This isn't rigorously tested. I used the following file and it seemed to work, skipping invalid values silently:
    Code:
    1 2 34 998 3 1 4
    2 4 
    3
    6
    77789 908 8 9 7 5
    asdf tr 4 9 2
    3 4 3 9 1 3 4 2 1 34 90 ad 4   
    5   9  1
    
    If you have more questions, please ask, but if this is homework please say so to begin with.

    -Lee

    EDIT: Please excuse the do while(.true.). I hate code like that, but using cycle/exit is easier, if a little dirtier. Sorry.

    EDIT 2: I thought i'd mention a design compromise i made. To simplify the logic in determining where the next value to read is, i made a sacrifice of execution time. This:
    Code:
    inp=trim(adjustl(inp(endpos+1:)))
    Is going to take some time to do, memory manipulation, etc. There's not a need to do this, but tracking both start and end position, and doing all of the slicing to achieve this, etc. was going to require too much brain power. If execution time is of critical importance, you could change the algorithm to do this with the line "fixed" with no modifications.

    EDIT 3: Bah, forget some things i wanted to mention. Company has been in town, i've been working on this on and off for the last two days, etc. What i wanted to add was that splitting up a string like this is best to have in a library that you can always use. At work we have something that will return the pointer to an array that's allocated to the appropriate size, with each item as an element. With something like that, it would be easy to just take each line, split it with that, and act on each element. Same goes for the code to add an element to an allocatable array. It's best to have one for each type, preferably with an interface so you can call the same name for all of your types, that just takes the current item count, the allocatable, and the value to add. I only implemented these things here since you wouldn't have them in a library.
     
  8. ssmmgg thread starter macrumors newbie

    Joined:
    Dec 26, 2009
    #8
    thanks

    i'm going to check it
    really tanx

    but it's not a home work
    it's a kind of work! ;)
     
  9. ssmmgg thread starter macrumors newbie

    Joined:
    Dec 26, 2009
    #9
    So

    but this happens:

    first 1
    and after exiting 2
     

    Attached Files:

    • 1.png
      1.png
      File size:
      38.3 KB
      Views:
      39
    • 2.png
      2.png
      File size:
      63.4 KB
      Views:
      36
  10. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #10
    I don't know what compiler/runtime you're using (certainly not running on a Mac...). I think the issue has to do with resizing of the allocatable arrays? Or doing assignment from arrays with mismatched dimensions? It might help to add slicing in the addVal copies to/from the temp array. For reference, this all works in g95.

    I can post the other code in the morning, but basically the change was, in checkline, if st is 0, do the btest and increment the appropriate count. Then the count line would come out, the array would come out, and addVal would come out. If you can't make the changes yourself, I'll post the other code tomorrow. It's a bit simpler, so it may work better with your compiler and runtime.

    -Lee

    edit:
    change
    allVals=tmpStore
    to:
    allVals(1:size(tmpStore))=tmpStore
    or even:
    allVals(1:size(tmpStore))=tmpStore(1:size(tmpStore))
    I think your runtime will be happier. The 7 and 31 threw me off, but it's really the first resize from 8 to 32 elements.
     
  11. ssmmgg thread starter macrumors newbie

    Joined:
    Dec 26, 2009
    #11
    thanks

    i used to do it on 7
    i'm trying any os
    thanx a lot
    really


    it works @ last! :apple:
     

Share This Page