Sweeping backward through formatted stream file - io

More problems with formatted stream I/O. This time I scan a file opened for formatted stream access for the starts of the lines. This is completely successful. However, when I try to copy the file backwards using these markers, problems are encountered both on ifort and gfortran. The program:
program testpos2
implicit none
character(80) halfline
character(:), allocatable :: line
integer iunit
integer junit
integer i
integer, parameter :: N = 4
integer pos(N)
integer size
integer current
open(newunit=iunit,file='testpos2.txt',status='replace')
do i = 1, N
write(halfline,'(*(g0))') i,' 2nd half ',i
write(iunit,'(*(g0))') trim(halfline)
end do
close(iunit)
allocate(character(len_trim(halfline)) :: line)
open(newunit=iunit,file='testpos2.txt',status='old',access='stream',form='formatted')
do i = 1, N
inquire(iunit,pos=pos(i))
read(iunit,'()')
end do
open(newunit=junit,file='testpos3.txt',status='replace')
do i = N, 1, -1
read(iunit,'(a)',pos=pos(i),advance='no',eor=10,size=size) line
inquire(iunit,pos=current)
write(*,'(*(g0))') 'pos(i) = ',pos(i),', current = ',current
write(junit,'(*(g0))',advance='no') line
do
read(iunit,'(a)',advance='no',eor=10,size=size) line
inquire(iunit,pos=current)
write(*,'(*(g0))') 'pos(i) = ',pos(i),', current = ',current
write(junit,'(*(g0))',advance='no') line
end do
10 continue
write(junit,'(*(g0))') line(1:size)
end do
end program testpos2
Run-time output with gfortran 8.1.0:
pos(i) = 43, current = 55
pos(i) = 29, current = 41
pos(i) = 15, current = 27
pos(i) = 1, current = 13
Perfect! but the output file is:
4 2nd half 4
3 2nd half 3
4 2nd half 4
1 2nd half 1
Observe that the third line is the wrong one!
Run-time output with ifort 16.0.2:
pos(i) = 43, current = 55
pos(i) = 29, current = 69
forrtl: severe (24): end-of-file during read, unit -130, file C:\testpos2.txt
And the output file is binary junk after the first line.
This looks much like the result of corrupted memory, but the program seems simple enough that I can't see where corruption could occur, not to mention that both ifort and gfortran produce the expected results when the file is scanned in the forward direction. So could it be that the I/O libraries of both compilers are the ones causing corruption or is it me?

Related

Calculate the delay of a company's employees and TypeError. /pychrm

`'''I'm new in Python and I've just started Python 3.9, but I wrote the following Python code, but it doesn't work properly and it gives me the following error: (I apologize in advance for writing my own English grammar)
unsupported operand type(s) for -: 'str' and 'str'
The question is to calculate the hourly delay of 20 employees, which should be written with the function and class for 20 people, so that two random numbers from each of the 20 people in the two time slots of 12-12 and 17-13 are written with the random function, but except for Thursday, which is only between 8:00-13:00 should take a random input time slot. And then add up the time delay of each person in one week and print their delay and do it in order until the 20th person and for each person the delay of each person is printed as the number of days or hours of delay.
I wrote the code, but the problem is that it gives a TypeError and says that these calculations are not possible for the string.'''
import random
c=0
def weeklyPaid(delays):
if delays >8:
return delays / 8
else:
return delays * 1
while c<=20:
sat1 = input(random.randint(8, 12))
sun1 = input(random.randint(8, 12))
mon1 = input(random.randint(8, 12))
tue1 = input(random.randint(8, 12))
wed1 = input(random.randint(8, 12))
thu1 = input(random.randint(8, 12))
sat2 = input(random.randint(13, 17))
sun2 = input(random.randint(13, 17))
mon2 = input(random.randint(13, 17))
tue2 = input(random.randint(13, 17))
wed2 = input(random.randint(13, 17))
thu2 = input(random.randint(13, 13))
delays = (sat2-sat1*1-8-1) + (sun2-sun1*1-8-1) +(mon2-mon1*1-8-1) + (tue2-tue1*1-8-1) + (wed2-wed1*1-8-1) + (thu2-thu1*1-8-1)
print("person delays","no",c+1, "=",delays)
c = c + 1
"If the prompt argument is present, it is written to standard output without a trailing newline. The function then reads a line from input, converts it to a string (stripping a trailing newline), and returns that. When EOF is read, EOFError is raised. "
this is from python docs. so I thought the reason of your issure is the type of the value from input is 'string', it can't plus with numbers.
your can use int() method to solve the problem.
e.g, sat2 = int(input(……))
a good method is use type(), when you are not sure the variant's type.
I'm puzzled about your code.
1st, the c+=1 will never be excuted, so the program will fall in endless loop, but you said only a error occured.
second, the function didn't be used
third, there is wrong indent in line 21 - 26
so, I suggest you re write your code

Setting up basic pararrayfun in GNU Octave

I have a file testPowr.m
function testPowr(x)
printf(x^2)
end
I am trying the following in another file main.m in the same folder as testPowr.m:
clear; clc;
pkg load parallel;
vector = 1:10;
fun = #(x) x^2;
pararrayfun(nproc, #(n) testPowr(n), vector)
%a2 = pararrayfun(nproc, fun, vector)
But testPowr seems not visible to pararrayfun as the commented line above works. I have tried multiple different syntax but I am unable to get it right.
I am getting the following error in one of the syntax:
execution error
error: __parcellfun_get_next_result__: could not receive result
error: called from
parcellfun at line 201 column 16
pararrayfun at line 85 column 28
main at line 5 column 1
The problem is that testPowr does not return a value.
pararrayfun collects the return value of each function call but all your function does is print the result to stdout. You can fix this by returning something, like so:
function xp = testPowr (x)
xp = x^2;
endfunction
Your script will then work fine:
octave> pkg load parallel;
octave> rv = pararrayfun (nproc, #(n) testPowr(n), 1:10)
parcellfun: 10/10 jobs done
rv =
1 4 9 16 25 36 49 64 81 100

Analyzing input from an internal unit in fortran [duplicate]

I have specific dataformat, say 'n' (arbitrary) row and '4' columns. If 'n' is '10', the example data would go like this.
1.01e+00 -2.01e-02 -3.01e-01 4.01e+02
1.02e+00 -2.02e-02 -3.02e-01 4.02e+02
1.03e+00 -2.03e-02 -3.03e-01 4.03e+02
1.04e+00 -2.04e-02 -3.04e-01 4.04e+02
1.05e+00 -2.05e-02 -3.05e-01 4.05e+02
1.06e+00 -2.06e-02 -3.06e-01 4.06e+02
1.07e+00 -2.07e-02 -3.07e-01 4.07e+02
1.08e+00 -2.08e-02 -3.08e-01 4.07e+02
1.09e+00 -2.09e-02 -3.09e-01 4.09e+02
1.10e+00 -2.10e-02 -3.10e-01 4.10e+02
Constraints in building this input would be
data should have '4' columns.
data separated by white spaces.
I want to implement a feature to check whether the input file has '4' columns in every row, and built my own based on the 'M.S.B's answer in the post Reading data file in Fortran with known number of lines but unknown number of entries in each line.
program readtest
use :: iso_fortran_env
implicit none
character(len=512) :: buffer
integer :: i, i_line, n, io, pos, pos_tmp, n_space
integer,parameter :: max_len = 512
character(len=max_len) :: filename
filename = 'data_wrong.dat'
open(42, file=trim(filename), status='old', action='read')
print *, '+++++++++++++++++++++++++++++++++++'
print *, '+ Count lines +'
print *, '+++++++++++++++++++++++++++++++++++'
n = 0
i_line = 0
do
pos = 1
pos_tmp = 1
i_line = i_line+1
read(42, '(a)', iostat=io) buffer
(*1)! Count blank spaces.
n_space = 0
do
pos = index(buffer(pos+1:), " ") + pos
if (pos /= 0) then
if (pos > pos_tmp+1) then
n_space = n_space+1
pos_tmp = pos
else
pos_tmp = pos
end if
endif
if (pos == max_len) then
exit
end if
end do
pos_tmp = pos
if (io /= 0) then
exit
end if
print *, '> line : ', i_line, ' n_space : ', n_space
n = n+1
end do
print *, ' >> number of line = ', n
end program
If I run the above program with a input file with some wrong rows like follows,
1.01e+00 -2.01e-02 -3.01e-01 4.01e+02
1.02e+00 -2.02e-02 -3.02e-01 4.02e+02
1.03e+00 -2.03e-02 -3.03e-01 4.03e+02
1.04e+00 -2.04e-02 -3.04e-01 4.04e+02
1.05e+00 -2.05e-02 -3.05e-01 4.05e+02
1.06e+00 -2.06e-02 -3.06e-01 4.06e+02
1.07e+00 -2.07e-02 -3.07e-01 4.07e+02
1.0 2.0 3.0
1.08e+00 -2.08e-02 -3.08e-01 4.07e+02 1.00
1.09e+00 -2.09e-02 -3.09e-01 4.09e+02
1.10e+00 -2.10e-02 -3.10e-01 4.10e+02
The output is like this,
+++++++++++++++++++++++++++++++++++
+ Count lines +
+++++++++++++++++++++++++++++++++++
> line : 1 n_space : 4
> line : 2 n_space : 4
> line : 3 n_space : 4
> line : 4 n_space : 4
> line : 5 n_space : 4
> line : 6 n_space : 4
> line : 7 n_space : 4
> line : 8 n_space : 3 (*2)
> line : 9 n_space : 5 (*3)
> line : 10 n_space : 4
> line : 11 n_space : 4
>> number of line = 11
And you can see that the wrong rows are properly detected as I intended (see (*2) and (*3)), and I can write 'if' statements to make some error messages.
But I think my code is 'extremely' ugly since I had to do something like (*1) in the code to count consecutive white spaces as one space. I think there would be much more elegant way to ensure the rows contain only '4' column each, say,
read(*,'4(X, A)') line
(which didn't work)
And also my program would fail if the length of 'buffer' exceeds 'max_len' which is set to '512' in this case. Indeed '512' should be enough for most practical purposes, I also want my checking subroutine to be robust in this way.
So, I want to improve my subroutine in at least these aspects
Want it to be more elegant (not as (*1))
Be more general (especially in regards to 'max_len')
Does anyone has some experience in building this kind of input-checking subroutine ??
Any comments would be highly appreciated.
Thank you for reading the question.
Without knowledge of the exact data format, I think it would be rather difficult to achieve what you want (or at least, I wouldn't know how to do it).
In the most general case, I think your space counting idea is the most robust and correct.
It can be adapted to avoid the maximum string length problem you describe.
In the following code, I go through the data as an unformatted, stream access file.
Basically you read every character and take note of new_lines and spaces.
As you did, you use spaces to count to columns (skipping double spaces) and new_line characters to count the rows.
However, here we are not reading the entire line as a string and going through it to find spaces; we read char by char, avoiding the fixed string length problem and we also end up with a single loop. Hope it helps.
EDIT: now handles white spaces at beginning at end of line and empty lines
program readtest
use :: iso_fortran_env
implicit none
character :: old_char, new_char
integer :: line, io, cols
logical :: beg_line
integer,parameter :: max_len = 512
character(len=max_len) :: filename
filename = 'data_wrong.txt'
! Output format to be used later
100 format (a, 3x, i0, a, 3x , i0)
open(42, file=trim(filename), status='old', action='read', &
form="unformatted", access="stream")
! set utils
old_char = " "
line = 0
beg_line = .true.
cols = 0
! Start scannig char by char
do
read(42, iostat = io) new_char
! Exit if EOF
if (io < 0) then
exit
end if
! Deal with empty lines
if (beg_line .and. new_char==new_line(new_char)) then
line = line + 1
write(*, 100, advance="no") "Line number:", line, &
"; Columns: Number", cols
write(*,'(6x, a5)') "EMPTYLINE"
! Deal with beginning of line for white spaces
elseif (beg_line) then
beg_line = .false.
! this indicates new columns
elseif (new_char==" " .and. old_char/=" ") then
cols = cols + 1
! End of line: time to print
elseif (new_char==new_line(new_char)) then
if (old_char/=" ") then
cols = cols+1
endif
line = line + 1
! Printing out results
write(*, 100, advance="no") "Line number:", line, &
"; Columns: Number", cols
if (cols == 4) then
write(*,'(6x, a5)') "OK"
else
write(*,'(6x, a5)') "ERROR"
end if
! Restart with a new line (reset counters)
cols = 0
beg_line = .true.
end if
old_char = new_char
end do
end program
This is the output of this program:
Line number: 1; Columns number: 4 OK
Line number: 2; Columns number: 4 OK
Line number: 3; Columns number: 4 OK
Line number: 4; Columns number: 4 OK
Line number: 5; Columns number: 4 OK
Line number: 6; Columns number: 4 OK
Line number: 7; Columns number: 4 OK
Line number: 8; Columns number: 3 ERROR
Line number: 9; Columns number: 5 ERROR
Line number: 10; Columns number: 4 OK
Line number: 11; Columns number: 4 OK
If you knew your data format, you could read your lines in a vector of dimension 4 and use iostat variable to print out an error on each line where iostat is an integer greater than 0.
Instead of counting whitespace you can use manipulation of substrings to get what you want. A simple example follows:
program foo
implicit none
character(len=512) str ! Assume str is sufficiently long buffer
integer fd, cnt, m, n
open(newunit=fd, file='test.dat', status='old')
do
cnt = 0
read(fd,'(A)',end=10) str
str = adjustl(str) ! Eliminate possible leading whitespace
do
n = index(str, ' ') ! Find first space
if (n /= 0) then
write(*, '(A)', advance='no') str(1:n)
str = adjustl(str(n+1:))
end if
if (len_trim(str) == 0) exit ! Trailing whitespace
cnt = cnt + 1
end do
if (cnt /= 3) then
write(*,'(A)') ' Error'
else
write(*,*)
end if
end do
10 close(fd)
end program foo
this should read any line of reasonable length (up to the line limit your compiler defaults to, which is generally 2GB now-adays). You could change it to stream I/O to have no limit but most Fortran compilers have trouble reading stream I/O from stdin, which this example reads from. So if the line looks anything like a list of numbers it should read them, tell you how many it read, and let you know if it had an error reading any value as a number (character strings, strings bigger than the size of a REAL value, ....). All the parts here are explained on the Fortran Wiki, but to keep it short this is a stripped down version that just puts the pieces together. The oddest behavior it would have is that if you entered something like this with a slash in it
10 20,,30,40e4 50 / this is a list of numbers
it would treat everything after the slash as a comment and not generate a non-zero status return while returning five values. For a more detailed explanation of the code I think the annotated pieces on the Wiki explain how it works. In the search, look for "getvals" and "readline".
So with this program you can read a line and if the return status is zero and the number of values read is four you should be good except for a few dusty corners where the lines would definitely not look like a list of numbers.
module M_getvals
private
public getvals, readline
implicit none
contains
subroutine getvals(line,values,icount,ierr)
character(len=*),intent(in) :: line
real :: values(:)
integer,intent(out) :: icount, ierr
character(len=:),allocatable :: buffer
character(len=len(line)) :: words(size(values))
integer :: ios, i
ierr=0
words=' '
buffer=trim(line)//"/"
read(buffer,*,iostat=ios) words
icount=0
do i=1,size(values)
if(words(i).eq.'') cycle
read(words(i),*,iostat=ios)values(icount+1)
if(ios.eq.0)then
icount=icount+1
else
ierr=ios
write(*,*)'*getvals* WARNING:['//trim(words(i))//'] is not a number'
endif
enddo
end subroutine getvals
subroutine readline(line,ier)
character(len=:),allocatable,intent(out) :: line
integer,intent(out) :: ier
integer,parameter :: buflen=1024
character(len=buflen) :: buffer
integer :: last, isize
line=''
ier=0
INFINITE: do
read(*,iostat=ier,fmt='(a)',advance='no',size=isize) buffer
if(isize.gt.0)line=line//buffer(:isize)
if(is_iostat_eor(ier))then
last=len(line)
if(last.ne.0)then
if(line(last:last).eq.'\\')then
line=line(:last-1)
cycle INFINITE
endif
endif
ier=0
exit INFINITE
elseif(ier.ne.0)then
exit INFINITE
endif
enddo INFINITE
line=trim(line)
end subroutine readline
end module M_getvals
program tryit
use M_getvals, only: getvals, readline
implicit none
character(len=:),allocatable :: line
real,allocatable :: values(:)
integer :: icount, ier, ierr
INFINITE: do
call readline(line,ier)
if(allocated(values))deallocate(values)
allocate(values(len(line)/2+1))
if(ier.ne.0)exit INFINITE
call getvals(line,values,icount,ierr)
write(*,'(*(g0,1x))')'VALUES=',values(:icount),'NUMBER OF VALUES=',icount,'STATUS=',ierr
enddo INFINITE
end program tryit
Honesty, it should work reasonably with just about any line you throw at it.
PS:
If you are always reading four values, using list-directed I/O and checking the iostat= value on READ and checking if you hit EOR would be very simple (just a few lines) but since you said you wanted to read lines of arbitrary length I am assuming four values on a line was just an example and you wanted something very generic.

Fortran find string in txt file

I would like to know how to find a string in a txt file and read the numbers after it.
The txt portion of the file is like:
...
x0 1 0 1 0.5 0
dx0 0 0 1 0 0
And here is what I'm trying to do:
character :: c
real :: v1(1:5), v2(1:5)
integer :: i
...
open(10, file="input.txt", action="read")
...
read(unit = 10, fmt=*) c
do while(c/='x')
read(unit = 10, fmt=*) c
end do
read(unit = 10, fmt=*) c
read(unit = 10, fmt=*) v1(1), v1(2), v1(3), v1(4), v1(5)
read(unit = 10, fmt=*) c c
read(unit = 10, fmt=*) v2(1), v2(2), v2(3), v2(4), v2(5)
close(10)
Is there any function that returns the position the do-while loop finds? What is a better way to find this position?
Thanks
The program below shows how to read numbers from a line where the search string is the first word. It uses an internal read, which is often useful in Fortran I/O.
program xinternal_read
implicit none
integer :: ierr
integer, parameter :: iu = 20
character (len=*), parameter :: search_str = "dx0"
real :: xx(5)
character (len=1000) :: text
character (len=10) :: word
open (unit=iu,file="foo.txt",action="read")
do
read (iu,"(a)",iostat=ierr) text ! read line into character variable
if (ierr /= 0) exit
read (text,*) word ! read first word of line
if (word == search_str) then ! found search string at beginning of line
read (text,*) word,xx
print*,"xx =",xx
end if
end do
end program xinternal_read
The output is
xx = 0. 0. 1. 0. 0.

How to write without a newline in Fortran?

I'm writing a piece of code by which I plan to write a .txt file that's a 10x10001 matrix:
do i = 1, 10
read(1,10) seed !Read a number from file 1
write(2,20) seed !Write that number in file 2
do k = 1, 10000
seed = mod((a*seed),m)
R = seed/m
write(2,20) R !I want all these numbers to be next to the seed, not on new lines
end do
end do
But all the R's are written on new lines.
Is there a way to separate every number with a space instead of a new line, or should I implement this same code using C++ and pipelines?
You can make write not add a newline at the end with the following code:
write(2, 20, advance="no") R

Resources