How to read string like a file in Fortran - fortran

Suppose I have a string like this :
character(20) :: str="&
&1.2 &
&2. &
&3.32 &
&4.223 &
"
How can I read the string like a file?
For example, I tried
program main
implicit none
character(20) :: str="&
&1.2 &
&2. &
&3.32 &
&4.223 &
"
integer i
real a
do i=1,4
read(str,*) a
print*, a
end do
end program main
but I get only the first line every time
1.200000
1.200000
1.200000
1.200000

You have four reals in your string, but you read it into one real variable (four times). Since every time you start with the same string, you always get the same result.
Maybe you should read the string into an array directly (without a loop):
real a(4)
read(str,*) a(1:4)
(You can simple write read(str,*) a, the range is just given to indicate that a is an array here...)

Related

Fortran program to return numbers from Integer input

After Receiving an integer as input from the keyboard, and passing it to a subroutine that should be responsible for returning the first number, how does one loop through the input and get the first value?
If they enter 123456 , I would need to return 6, then 5, then 3, then 1.in different subroutines.
PROGRAM DATING
IMPLICIT NONE
INTEGER :: num, first, second, fourth, sixth ! varialbe declaration
CHARACTER(Len=10) :: output
WRITE *,' What number did prison Ex give you? '
READ *, num
CALL FIRST( + num)
CALL SECOND( + second)
CALL FOURTH( + fourth)
CALL SIXTH( + sixth)
WRITE(output,'(i2.2)')num ! program output
WRITE *, "Use" ! program output
END PROGRAM DATING
SUBROUTINE FIRST(first)
IMPLICIT NONE
RETURN
END SUBROUTINE FIRST
SUBROUTINE SECOND(Second)
IMPLICIT NONE
RETURN
END SUBROUTINE SECOND
SUBROUTINE FOURTH(fourth)
IMPLICIT NONE
RETURN
END SUBROUTINE FOURTH
SUBROUTINE SIXTH(sixth)
IMPLICIT NONE
RETURN
END SUBROUTINE SIXTH
I have tried to develop subroutines to handle each case but looping through input is tricky
If you want to read six integer digits from a string with six digits, you can read them directly:
integer :: digits(6)
read(*,'(6i1)') digits
You can also convert an existing integer number to a string and do the same:
character(6) :: string
write(string,*) num
read(string,'(6i1)') digits
And you can also compute the digits from the integer using division by 10 and remainders after division:
sixth = mod(num, 10)
num = num/10
fifth = mod(num, 10)
num = num/10
...
Or just make a loop and use the digits array. The above just shows the main idea.
You must make sure that there are indeed six digits or implement some checks.

How to read a file in fortran by using a while loop?

I am trying to read a text file using a Fortran code. I have a file with 1999 rows and the number of columns vary with each row. Can someone please tell me how one can code such a problem. This is my code for reading a 4*2 text file but I am using do loops which I can't use in my current case.
PROGRAM myread2
IMPLICIT NONE
INTEGER, DIMENSION(100) :: a, b
INTEGER :: row,col,max_rows,max_cols
OPEN(UNIT=11, file='text.txt')
DO row = 1,4
READ(11,*) a(row), b(row)
END DO
PRINT *, a(1)
PRINT *, a(4)
PRINT*, b(4)
END PROGRAM myread2
The best way of reading a file like this depends on how you want to store the data. I'm going to use a ragged array as it's probably simplest, although other container types may be better suited depending on your requirements.
Fortran doesn't have ragged arrays natively, so first you need to define a type to hold each row. This can be done as
type :: RowData
integer, allocatable :: cols(:)
end type
type(RowData), allocatable :: rows(:)
When this container is filled out, the value in the i'th column of the j'th row will be accessed as
value = rows(j)%cols(i)
We can then write a program to read the file, e.g.
type :: RowData
integer, allocatable :: cols(:)
end type
type(RowData), allocatable :: rows(:)
integer :: no_rows
integer :: i
open(unit=11, file='text.txt')
no_rows = count_lines(11)
allocate(rows(no_rows))
do i=1,no_rows
rows(i)%cols = read_row(11)
enddo
Now we just need to write the functions count_lines, which counts the number of lines in the file, and read_row, which reads a line from the file and returns the contents of that line as an array of integers.
Following this question, count_lines can be written as
! Takes a file unit, and returns the number of lines in the file.
! N.B. the file must be at the start of the file.
function count_lines(file_unit) result(output)
integer, intent(in) :: file_unit
integer :: output
integer :: iostat
output = 0
iostat = 0
do while (iostat==0)
read(file_unit, *, iostat=iostat)
if (iostat==0) then
output = output+1
endif
enddo
rewind(file_unit)
end function
Writing read_row, to parse a line of unknown length from a file, can be done by following this question.

Fortran character(:), dimension(:) vs character, dimension(:,:)

I was trying to copy contents of a file to a string array and I couldn't manage to fully copy the file (it was only copying the first characters in every line). I feel like something is wrong with my syntax and its possible to do it with character, dimension(:,:) but it worked with character(:), dimension(:).
This doesn't work as expected:
character, allocatable :: list(:,:)
integer :: i, line_count, line_length
open(1, "input.txt", status="old", action="read")
line_count = count_file_lines(1) ! function that returns integer
line_length = longest_line_length(1) ! function that returns integer
allocate(list(line_count, line_length))
do i = 1, line_count
read(1, *) list(i,:)
end do
close(1)
This works as expected:
character(:), allocatable :: list(:)
integer :: i, line_count, line_length
open(1, "input.txt", status="old", action="read")
line_count = count_file_lines(1) ! function that returns integer
line_length = longest_line_length(1) ! function that returns integer
allocate(character(line_length) :: list(line_count))
do i = 1, line_count
read(1, *) list(i)
end do
close(1)
I've tried switching indexes in the first example and it still didn't work. I understand that the first example is a rank 2 character array but what is the array in the second example? Maybe they both are the same type of array and I got the indexing wrong for the first one. Can someone clarify this?
In the first example you have a 2D array of single characters, of character strings of size 1. In the other example you have a 1D array of longer character strings. See Difference between "character*10 :: a" and "character :: a(10)" for the difference.
The readstatement regards each of the character in the 2D array as a separate variable it tries to read. That is why it appears storing only the first character each time. The list-directed format * you are using is not good enough for reading a character array.
You can actually read a line to a character array, but you have to read it as an array and use the appropriate format
read(1, '(*(a))') str(i,:)
You are responsible to make sure that three are enough characters on each line of your file for your arrays.
You must also be careful when printing the content, which you do not show.
Be aware that using unit 1 for your files is poor form. Unit numbers below 10 are often pre-connected by the compiler to standard input, standard output, standard error and possibly other files.

How can I create a function in Fortran, that reads from file and works with it [duplicate]

This question already has answers here:
Passing character strings of different lengths to functions in Fortran
(2 answers)
Closed 2 years ago.
I have a bunch of files, which contain numbers in rows. I need to write a function, that
reads from file for the first time to find amount of elements in file;
allocates an array and reads numbers from file into array;
returns an array
My function gets a string - name of the file - as an input.
So, the function that I wrote is:
function arrays_proc(name) result(arr)
character(len=128), intent(in) :: name
integer :: i, tmp, ios
character(len=30) :: line
double precision, dimension(:), allocatable :: arr
open(unit=09, file=name, status='old', iostat=ios)
if ( ios /= 0) stop "error opening file"
tmp = 0
do
read(09, '(A)', iostat=ios) line
if (ios /= 0) exit
tmp = tmp + 1
end do
allocate(arr(tmp))
rewind(09)
do i=1, tmp
read(09, '(A)') arr(i)
end do
close(09)
return
end function arrays_proc
Then, in the main program I write
...
real(8), dimension(:), allocatable :: points, potent
points = arrays_proc(trim('carbon_mesh.txt'))
potent = arrays_proc(trim('carbon_pot.txt'))
...
When I run my program, I get instant "error opening file".
I assume the problem is with names of files or how I put them in my function.
Anyway, I hope someone can help me
When compiling your code with a minimal program, GFortran prints the following warnings:
a.f90:4:25:
4 | points = arrays_proc(trim('carbon_mesh.txt'))
| 1
Warning: Character length of actual argument shorter than of dummy argument ‘name’ (15/128) at (1)
a.f90:5:25:
5 | potent = arrays_proc(trim('carbon_pot.txt'))
| 1
Warning: Character length of actual argument shorter than of dummy argument ‘name’ (14/128) at (1)
Trying to print the value of name inside arrays_proc shows that it is filled with garbage. So, guided by the warnings, you can try to change the length of the name parameter to *, which allows a string of any length to be used as input.
With that change, the function manages to open the files.
See also: Passing character strings of different lengths to functions in Fortran

Write to file, keep changes and append to them

I have a program that creates values for the matrix u, and this changes for every iteration f, I want to write out the value of u(2,2) for every iteration f. So for example u(2,2)=5 f=1, u(2,2)=9 f=2, and so on.
Now test(u,n,f) only writes the last value.When it have met my criteria to stop the do loop. I don't want my subroutine to overwrite the file plot.txt every time, I want it to keep u(2,2) for every iterations. I want it to look like this
5 1
9 2
10 3
but not it only writes
15 25
How can this be fixed?
subroutine test(u,n,f)
!input
integer :: n,f,write_unit
real(8) :: u(n+2,n+2)
!lokale
integer :: i,j
real(8) :: vek_x,vek_y
!Skriver vektor verdier til fil som gnuplot skal bruke
open(newunit=write_unit,access='sequential',file='plot.txt',status='unknown')
write(write_unit,*)'# x y vx vy'
vek_x=u(2,2)
!write(write_unit,*) vek_x,f
write(write_unit,*) vek_x,f
write(write_unit,*)''
close(write_unit,status='keep')
"Program" that creates different values for u
do f=1,1000
do j=2,n+1
do i=2,n+1
u(i,j)=(u(i+1,j)+u(i-1,j)+u(i,j+1)+u(i,j-1))/4
!u(i,j)=(1-omega)*u(i,j)+omega*1/4*(u(i+1,j)+u(i-1,j)+u(i,j+1)+u(i,j-1))
end do
end do
if (u(2,2) .eq. 15) then
exit
end if
call test(u,n,f)
end do
Just open the file for appending
open(newunit=write_unit,access='sequential',file='plot.txt',position='append',status='old',action='write')
if that is what you wanted.
For the first time to may want to just create it empty
open(newunit=write_unit,access='sequential',file='plot.txt',status='replace')
close(write_unit)