Read text file line by line but only specific columns - file-io

How do we read a specific file line by line while skipping some columns in it?
For example, I have a text file which has data, sorted out in 5 columns, but I need to read only two columns out of it, they can be first two or any other random combination (I mean, need a solution which would work with any combination of columns like first and third only).
Code something like this
open(1, file=data_file)
read (1,*) ! to skip first line, with metadata
lmax = 0
do while (.true.)
! read column 1 and 3 here, either write
! that to an array or just loop through each row
end do
99 continue
close (1)
Any explanation or example would help a lot.

High Performance Mark's answer gives the essentials of simple selective column reading: one still reads the column but transfers it to a then-ignored variable.
To extend that answer, then, consider that we want to read the second and fourth columns of a five-column line:
read(*,*) junk, x, junk, y
The first value is transferred into junk, then the second into x, then the third (replacing the one just acquired a moment ago) into junk and finally the fourth into y. The fifth is ignored because we've run out of input items and the transfer statement terminates (and the next read in a loop will go to the next record).
Of course, this is fine when we know it's those columns we want. Let's generalize to when we don't know in advance:
integer col1, col2 ! The columns we require, defined somehow (assume col1<col2)
<type>, dimension(nrows) :: x, y, junk(3) ! For the number of rows
integer i
do i=1,nrows
read(*,*) junk(:col1-1), x(i), junk(:col2-col1-1), y(i)
end do
Here, we transfer a number of values (which may be zero) up to just before the first column of interest, then the value of interest. After that, more to-be-ignored values (possibly zero), then the final value of interest. The rest of the row is skipped.
This is still very basic and avoids many potential complications in requirements. To some extent, it's such a basic approach one may as well just consider:
do i=1,nrows
read(*,*) allofthem(:5)
x(i) = allofthem(col1)
y(i) = allofthem(col2)
end do
(where that variable is a row-by-row temporary) but variety and options are good.

This is very easy. You simply read 5 variables from each line and ignore the ones you have no further use for. Something like
do i = 1, 100
read(*,*) a(i), b, c(i), d, e
end do
This will overwrite the values in b, d, and e at every iteration.
Incidentally, your line
99 continue
is redundant; it's not used as the closing line for the do loop and you're not branching to it from anywhere else. If you are branching to it from unseen code you could just attach the label 99 to the next line and delete the continue statement. Generally, continue is redundant in modern Fortran; specifically it seems redundant in your code.

Related

I would like to MOVE just one line in Vim

yy and p should copy and paste 1 line of text.
But I have to go back and delete the original line.
:2,5m10
should move lines from 2 to 5 to line 10. however I need to enable :set number
to see what lines I am moving
I would like to move just 1 line of text, sort of like yy+p and not use :2,3m10
to move just one line.
Is there something like mm+p ?
so it copies the current line into buffer and deletes the line and you p paste it where you want ?
:3m . moves line 3 to your current line.
Above line does the function I want. Can I set a key mapping so
that "mm" replaces ":3m." ? I find it easier to type. TIA
What you're describing is the default behaviour when using dd -it deletes a
line into the buffer and p will paste it.
So dd and p works.
If you're new to vim, then it might seem a little strange that 'yanking' (with
y) and 'deleting' (with d) both copy to the buffer, given the 'cut', 'copy'
and 'paste' behaviours of most other editors.
You can read more about it with :help change.txt and in particular :help registers.
Also, since you say you need to enable :set number, I wonder if you've come
across :set relativenumber? This is very useful - in the example below, the
numbers would look this way if the your cursor was on the line with
'demonstrate':
3 This is just
2 a small
1 example to
0 demonstrate
1 how relative
2 numbers can
3 be useful
Thus if you wanted to move the line 'a small' below the line with 'numbers
can', you could use the relative line numbers to know that 2k would put the
cursor on the line you want, where you'd hit dd, then you'd have this
situation (the deleted line is now in the buffer:
1 This is just
0 example to
1 demonstrate
2 how relative
3 numbers can
4 be useful
Then you can do 3j to move to the 'numbers can' line, and hit p. So
relative numbers are a nice way to move quickly to lines you can see. Also,
just for completeness, you can use relative numbers in a similar way on the
command line::-2m+3 (although I know this isn't what you're after). You can
even set both relative number and set number at the same time, in which case
it's like in the example above, only you have the absolute line number
displayed on the current line instead of a zero.

Cannot print subsequent rows of array to file

I'm trying to write a rudimentary bit of code to print a 50*50 array called 'arr'. Unfortunately it so far only prints the first row of the array, although the formatting for that row is correct. I've attached the code below and was wondering if anyone could point out where I was going wrong? Thank you!
program testing
implicit none
integer :: i, j
integer, dimension (1:50, 1:50) :: arr
arr = 1
do i=1,50
open(unit=6, file= "array.txt", action="write")
write(6, '(2500I3)') (arr(i,j), j=1,50)
close(6)
end do
end program testing
Your open statement is inside loop (along with a matching close statement). That means for every row of the array, you open the file. That's probably not what you meant to do.
The default position specifier for an OPEN statement if there is no POSITION specifier is 'ASIS'. For a file that already exists (your case after the first iteration, and perhaps even for the first iteration) that means that the position is unspecified. Your processor probably takes that to be the start of the file. That means that each iteration of the loop you simply overwrite the first record, over and over again.
If you must open the file each iteration, then use the POSITION='APPEND' specifier to position the file at the end when the open statement is executed. Otherwise, move the open and close statements out of the loop.
(The way that the default of 'ASIS' behaves means that you should always specify the initial position of a file via a POSITION specifier when executing an OPEN statement for an existing "on disk" file.)
IanH's answer is correct. Your program can be fixed as follows. Note that output units should be parameterized and not set to 6 and that arrays and array sections can be written as shown.
program testing
implicit none
integer :: i
integer, dimension (1:50, 1:50) :: arr
integer, parameter :: outu = 20 ! better to parameterize unit and
! not to use the number 6, which most compilers
! use for standard output
arr = 1
open(unit=outu, file= "array.txt", action="write")
do i=1,50
write(outu, '(2500I3)') arr(i,:) ! can write array section without implied do loop
end do
close(outu)
end program testing

Write data to file in columns (Fortran)

I need to write some data to file in Fortran 90. How should I use WRITE (*,*) input to have the values grouped in columns? WRITE always puts a new line after each call, that's the problem.
code example:
open (unit = 4, file = 'generated_trajectories1.dat', form='formatted')
do time_nr=0, N
write (4,*) dble(time_nr)*dt, initial_traj(time_nr)
end do
And now the point is to have it written in separate columns.
You can use implied DO loops to write values as single records. Compare the following two examples:
integer :: i
do i=1,10
write(*,'(2I4)') i, 2*i
end do
It produces:
1 2
2 4
3 6
...
Using implied DO loops it can rewritten as:
integer :: i
write(*, '(10(2I4))') (i, 2*i, i=1,10)
This one produces:
1 2 2 4 3 6 ...
If the number of elements is not fixed at compile time, you can either use the <n> extension (not supported by gfortran):
write(*, '(<n>(2I4))') (i, 2*i, i=1,n)
It takes the number of repetitions of the (2I4) edit descriptor from the value of the variable n. In GNU Fortran you can first create the appropriate edit descriptor using internal files:
character(len=20) :: myfmt
write(myfmt, '("(",I0,"(2I4))")') n
write(*, fmt=myfmt) (i, 2*i, i=1,n)
Of course, it also works with list directed output (that is output with format of *):
write(*, *) (i, 2*i, i=1,10)
This really depends on what data you are trying to write to file (i.e. whether you have a scalar within a loop or an array...). Can you include a description of this in your question?
If your are trying to write a scalar multiple times to the same row then try using non-advancing I/O, passing the keyword argument advance="no" to the write statement, e.g.
integer :: x
do x = 1,10
write(*, fmt='(i4,1x)', advance="no") x
end do
However, be aware of a suprise with non-advancing I/O.
The answer depends on your answer to Chris's question. If you want a single line, then you will have to use non-advancing IO as described by Chris. Without this, with multiple formatted write statement you will always get multiple lines.
Also, you will likely need to use formatted IO instead of list-directed (*) IO. The rules are loose for list-directed IO. Different compilers may produce different output. With many output items, line breaks are likely to keep the output lines from being too long.
Here a format that should work if all of your variables are reals:
write (4, '( *(2X, ES14.6) )', advance="no" )
how about the good old $ edit descriptor:
write(*, fmt='(i4,$)') x
remember to do a write(*,*) after your loop...

Fortran, How do I get Fortran to ignore lines from a data file that has random spacing

I am writing a FORTRAN code that uses data in a file made by a MD program. the data is a list of values but has breaks in the data for list updates in the form (# Neighbor list update .. 6527 indexes in list), These breaks are at random intervals so I can't just skip every x
I when I do my code it doesn't ignore these lines and randomly adds the value from the previous step.
1, 0.98510699999999995, 0.98510699999999995
2, 1.9654170000000000, 0.98031000000000001
3, 2.9427820000000002, 0.97736500000000004
4, 3.9186540000000001, 0.97587199999999996
4, 4.8945259999999999, 0.97587199999999996
5, 5.8697910000000002, 0.97526500000000005
note the double step 4 with an identical value from the true step 4
How would I go about skipping this line. Please find the sample code below
Open(Unit=10,File='prod._100.tup')
do i=1,50
Read(10,*,IOSTAT=ios)step,temp,kinetic,potential,total,pressure
If(IS_IOSTAT_END(ios)) Exit
test=test+temp
print*, step, test, temp
End Do
It is not clear to me what the "breaks" in the file are. Are they blank lines? If so, the following code should work:
use, intrinsic :: iso_fortran_env
character (len=200) :: line
Open(Unit=10,File='prod._100.tup')
read_loop: do
Read (10,'(A)',IOSTAT=ios) line
If(ios == iostat_end) exit read_loop
if (len_trim (line) == 0) then
write (*, *) "blank line"
cycle read_loop
end if
read (line, *) step,temp,kinetic,potential,total,pressure
test=test+temp
print*, step, test, temp
end do: read_loop
write (*, *) "total is", test
The above is not tested. The "len_trim" test is based on bad records being blank lines. If breaks are otherwise defined you will have to create a different test.
Try:
i=1
do while (i<=50)
Read(10,*,IOSTAT=ios)step,temp,kinetic,potential,total,pressure
If(IS_IOSTAT_END(ios)) Exit
IF(ios.ne.0) cycle
test=test+temp
i=i+1
enddo
When a bad record is read, ios is assigned a system dependent non-zero number (it is zero on success). Apparently you've written a function (IS_IOSTAT_END) to tell if you've reached the end of the file, but other error conditions can exist (for example, the read statement doesn't match the data). That will return a different non-zero ios than an end-file record, so you should just restart the loop at that point (e.g. cycle)
I assume you want to read exactly 50 lines from the file, so I changed your do loop to a do while, but if the number of records you read doesn't actually matter, then feel free to change it back.

Fortran: How do I read the first character from each line of a text file?

this is my first time trying to program in Fortran. I'm trying to write a program that prints the first 1476 terms of the Fibonacci sequence, then examines the first digit of each term and stores the number of 1s, 2s, 3s, ..., 9s that occur in an array.
The problem that I can't seem to figure out is how to read the first digit of each term. I've tried several things but am having difficulty with my limited knowledge of Fortran techniques. I write the terms to a text file and the idea is to read the first digit of each line and accumulate the respective number in the array. Does anyone have any suggestions of how to do this?
Here is my code so far:
(edit: I included the code I have for reading the file. Right now it just prints out 3.60772951994415996E-313,
which seems like an address of some sort, because it's not one of the Fibonacci numbers. Also, it is the only thing printed, I expected that it would print out every line of the file...)
(edit edit: After considering this, perhaps there's a way to format the writing to the text file to just the first digit. Is there a way to set the number of significant digits of a real number to one? :P)
subroutine writeFib(n)
integer :: i
real*8 :: prev, current, newFib
prev = 0
current = 1
do i = 1, n
newFib = prev + current
prev = current
current = newFib
write(7,*) newFib
end do
return
end subroutine
subroutine recordFirstDigits(a)
integer :: openStat, inputStat
real*8 :: fibNum
open(7, file = "fort.7", iostat = openStat)
if (openStat > 0) stop "*** Cannot open the file ***"
do
read(7, *, iostat = inputStat) fibNum
print *,fibNum
if (inputStat > 0) stop "*** input error ***"
if (inputStat < 0) exit ! end of file
end do
close(7)
end subroutine
program test
integer :: k, a(9)
k = 1476
call writeFib(k)
call recordFirstDigits(a)
end program
Although the suggestions were in place, there were also several things that were forgotten. Range of the REAL kind, and some formatting problems.
Anyways, here's one patched up solution, compiled and working, so try to see if this will work for you. I've took the liberty of choosing my own method for fibonacci numbers calculation.
program SO1658805
implicit none
integer, parameter :: iwp = selected_real_kind(15,310)
real(iwp) :: fi, fib
integer :: i
character(60) :: line
character(1) :: digit
integer :: n0=0, n1=0, n2=0, n3=0, n4=0, n5=0, n6=0, n7=0, n8=0, n9=0
open(unit=1, file='temp.txt', status='replace')
rewind(1)
!-------- calculating fibonacci numbers -------
fi = (1+5**0.5)/2.
do i=0,1477
fib = (fi**i - (1-fi)**i)/5**0.5
write(1,*)fib,i
end do
!----------------------------------------------
rewind(1)
do i=0,1477
read(1,'(a)')line
line = adjustl(line)
write(*,'(a)')line
read(line,'(a1)')digit
if(digit.eq.' ') n0=n0+1
if(digit.eq.'1') n1=n1+1
if(digit.eq.'2') n2=n2+1
if(digit.eq.'3') n3=n3+1
if(digit.eq.'4') n4=n4+1
if(digit.eq.'5') n5=n5+1
if(digit.eq.'6') n6=n6+1
if(digit.eq.'7') n7=n7+1
if(digit.eq.'8') n8=n8+1
if(digit.eq.'9') n9=n9+1
end do
close(1)
write(*,'("Total number of different digits")')
write(*,'("Number of digits 0: ",i5)')n0
write(*,'("Number of digits 1: ",i5)')n1
write(*,'("Number of digits 2: ",i5)')n2
write(*,'("Number of digits 3: ",i5)')n3
write(*,'("Number of digits 4: ",i5)')n4
write(*,'("Number of digits 5: ",i5)')n5
write(*,'("Number of digits 6: ",i5)')n6
write(*,'("Number of digits 7: ",i5)')n7
write(*,'("Number of digits 8: ",i5)')n8
write(*,'("Number of digits 9: ",i5)')n9
read(*,*)
end program SO1658805
Aw, ... I just read you need the number of digits stored in to an array. While I just counted them.
Oh well, ... "left as an exercise for the reader ..." :-)
Can you read with a FORMAT(A1)? It's been 20 years so I don't remember the exact syntax.
I wonder why the open statement succeeds when file 7 hasn't been closed. I think you need an endfile statement and/or a rewind statement in between writing and reading.
Paul Tomblin posted what you have to do after you solve your problem in getting reads to work in the first place.
I am getting an 'end of line' runtime error
You don't show the ! code to read here... which makes it kind of difficult to guess what you are doing wrong :-)
Perhaps you need a loop to read each line and then jump out of the loop to a continue statement when there are no more lines.
Something like this:
do
read(7,*,end=10) fibNumber
end do
10 continue
Better still - look at the more modern style used in this revcomp program.
here are some hints:
You don't need to use characters,
much less file i/o for this problem
(unless you forgot to state that a
file must be created).
Therefore, use math to find your statistics. There are lots of resources on Fibonacci numbers that might provide a simplifying insight or at least a way to independently spot check your answers.
Here is a complicated hint in non-Fortran lingo:
floor(10^(frac(log_10(7214989861293412))))
(Put this in Wolfram Alpha to see what it does.)
A simpler hint (for a different approach) is that you can do very
well in Fortran with simple
arithmetic inside of looping
constructs--at least for a first pass at the solution.
Accumulate your statistics as you
go. This advice would even apply to your character-driven approach. (This problem is ideally suited
for coming up with a cute indexing
scheme for your statistics, but some
people hate cute schemes in
programming. If you don't fear
cuteness ... then you can have associative
arrays in Fortran as long as your
keys are integers ;-)
The most important aspect of this
problem is the data type you will
use to calculate your answers. For
example, here's the last number you
will have to print.
Cheers, --Jared