Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

ssmmgg

macrumors newbie
Original poster
Dec 26, 2009
6
0
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
 
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
 
show me an example

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

without an allocable array. :)
 
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
 
i can't

i forced to do it with read command
nothing else allowed:(
 
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
 
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.
 
thanks

i'm going to check it
really tanx

but it's not a home work
it's a kind of work! ;)
 
So

but this happens:

first 1
and after exiting 2
 

Attachments

  • 1.png
    1.png
    38.3 KB · Views: 86
  • 2.png
    2.png
    63.4 KB · Views: 81
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.
 
thanks

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


it works @ last! :apple:
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.