Read scientific formatted numbers from txt

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP


Read scientific formatted numbers from txt



I would like to read and store scientific formatted numbers from a txt file, which is formatted and the numbers are separated by tabulator.



This is what I have so far:


IMPLICIT NONE

REAL,ALLOCATABLE,DIMENSION(2) :: data(:,:)
INTEGER :: row,column
INTEGER :: j,i
CHARACTER(len=30) :: filename
CHARACTER(len=30) :: format

filename='data.txt'
open(86,file=filename,err=10)
write(*,*)'open data file'
read(86, *) row
read(86, *) column

allocate(data(row,column))

format='(ES14.7)'

do i=1,row
read(86,format) data(i,:)
enddo

close(86)



This is how the txt file looks like:


200
35
2.9900E-35 2.8000E-35 2.6300E-35 2.4600E-35 2.3100E-35 2.1600E-35 ...



The problem is that it doesn't read and store the correct values from the txt to the data variable. Is it the format causing the problem?



I would also like to know how to count the number of columns in this case. (I can count the rows by using read(86,*) in a for loop.)





This recent question may be of interest, as format reversion is potentially important: your format requests a single value from a record/line. If it isn't, then can you explain exactly what unexpected behaviour you see?
– francescalus
Oct 15 '16 at 12:05





is seems like you do know the number of columns (35). clarify why you want to count the columns?
– agentp
Oct 15 '16 at 12:50


(35)





I know the number of columns in case of this specific file, but I would like to process maybe tens or hundreds of data files like that. The point would be not to check each and set it by hand.
– user2346329
Oct 15 '16 at 13:59





have you tried double precision or real(8) instead of real?
– ja72
Oct 16 '16 at 0:07


double precision


real(8)


real





Now you see why it is bad to put two questions into one post. You will get answers answering just one part. Always ask one question per post. How is one supposed to accept one I don't know.
– Vladimir F
Oct 16 '16 at 8:21






2 Answers
2



Yes, your format is not good for the data you show. Better one should be like that read(99,'(6(E11.4,X))') myData(i,:).


read(99,'(6(E11.4,X))') myData(i,:)



However, I am not sure if you really need to use format at your reading at all.



Following example pretty close to what you are trying to do, and it is working bot with and without format.


program readdata
implicit none
real, allocatable :: myData(:,:)
real :: myLine
integer :: i, j, myRow, myColumn
character(len=30) :: myFileName
character(len=30) :: myFormat

myFileName='data.dat'

open(99, file=myFileName)
write(*,*)'open data file'
read(99, *) myRow
read(99, *) myColumn

allocate(myData(myRow,myColumn))

do i=1,myRow
read(99,*) myData(i,:)
!read(99,'(6(E11.4,X))') myData(i,:)
print*, myData(i,:)
enddo

close(99)

end program readdata



To test, I assumed that you have rows and columns always in the file, as you give, so my test data was following.


2
6
2.9900E-35 2.8000E-35 2.6300E-35 2.4600E-35 2.3100E-35 2.1600E-35
2.9900E-35 2.8000E-35 2.6300E-35 2.4600E-35 2.3100E-35 2.1600E-35



If you are really interested to read your files with a format and if the number of columns are not constant you may need a format depending on a variable, please see related discussions here.





Thank you for the answer! I will need to refresh my knowledge about formats :)
– user2346329
Oct 17 '16 at 12:09



Though there are no direct command to count the number of items in a line, we can count the number of periods or (E|e|D|d) by using the scan command. For example,


program main
implicit none
character(100) str
integer n
read( *, "(a)" ) str

call countreal( str, n )
print *, "number of items = ", n
contains

subroutine countreal( str, num )
implicit none
character(*), intent(in) :: str
integer, intent(out) :: num
integer pos, offset

num = 0
pos = 0
do
offset = scan( str( pos + 1 : ), "." ) !! (1) search for periods
!! offset = scan( str( pos + 1 : ), "EeDd" ) !! (2) search for (E|e|D|d)

if ( offset > 0 ) then
pos = pos + offset
num = num + 1
print *, "pos=", pos, "num=", num !! just for check
else
return
endif
enddo
endsubroutine
end



Please note that pattern (1) works only when all items have periods, while pattern (2) works only when all items have exponents:


# When compiled with (1)

$ echo "2.9900 2.8000E-35 2.6300D-35 2.46 2.31" | ./a.out
pos= 2 num= 1
pos= 10 num= 2
pos= 22 num= 3
pos= 34 num= 4
pos= 40 num= 5
number of items = 5

# When compiled with (2)

$ echo "2.9900E-35 2.8000D-35 2.6300e-35 2.4600d-35" | ./a.out
pos= 7 num= 1
pos= 19 num= 2
pos= 31 num= 3
pos= 43 num= 4
number of items = 4



For more general purposes, it may be more convenient to write a custom "split()" function that separate items with white spaces (or use an external library that supports a split function).





Thank you for the subroutine! Haven't used scan before, but seems as the easiest solution.
– user2346329
Oct 18 '16 at 9:13






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Keycloak server returning user_not_found error when user is already imported with LDAP

PHP parse/syntax errors; and how to solve them?

How to scale/resize CVPixelBufferRef in objective C, iOS