I have a Fortran project that I am working on that requires scanning through a database and storing strings that only contain characters specified by a reference string.
For example, supposed I have a reference string "A11." The database contains several pieces of data some of which contain all of the characters in the reference and some that do not, the following set is an example of this:
"A111,
"A211"
"B11"
"1AA1"
In this case, the program only pulls "A111," and "1AA1," because the other strings contain characters not in the reference string "A11."
I tried using a variant of this code, but it doesn't seem to work. Any help would be greatly appreciated.
program main
implicit none
integer :: iostatus, i
character*6, dimension(:), allocatable :: mystr_temp
character*6 :: refstr
open(unit=2,file='file.txt')
iostatus = 1
i = 1
refstr = 'Ref'
mystr(1) = ''
do while (iostatus > 0)
do while (any(mystr(i)) /= any(refstr))
read(2,'(A6)', iostat = iostatus) mystr(i)
end do
i = i + 1
end do
end program main
We can probably do this by using verify(str1,str2), which returns the location of the first character in str1 not present in str2. If all the characters in str1 are found in str2, the function returns 0. So I have modified the code such that
program main
implicit none
integer :: ios, i
character(6) :: mystr(100), refstr, stmp
open(unit=2,file='file.txt')
i = 0
refstr = 'A11'
do
read(2, '(a6)', iostat=ios) stmp
if ( ios /= 0 ) exit
if ( verify( stmp, refstr ) == 0 ) then
i = i + 1
mystr(i) = stmp
print *, i, ":", mystr(i)
endif
enddo
end program
which gives for the sample data in the question
1 :A111
2 :1AA1
Related
I have a program in Fortran that saves the results to a file. At the moment I open the file using
OPEN (1, FILE = 'Output.TXT')
However, I now want to run a loop, and save the results of each iteration to the files 'Output1.TXT', 'Output2.TXT', 'Output3.TXT', and so on.
Is there an easy way in Fortran to constuct filenames from the loop counter i?
you can write to a unit, but you can also write to a string
program foo
character(len=1024) :: filename
write (filename, "(A5,I2)") "hello", 10
print *, trim(filename)
end program
Please note (this is the second trick I was talking about) that you can also build a format string programmatically.
program foo
character(len=1024) :: filename
character(len=1024) :: format_string
integer :: i
do i=1, 10
if (i < 10) then
format_string = "(A5,I1)"
else
format_string = "(A5,I2)"
endif
write (filename,format_string) "hello", i
print *, trim(filename)
enddo
end program
A much easier solution IMHO ...................
character(len=8) :: fmt ! format descriptor
fmt = '(I5.5)' ! an integer of width 5 with zeros at the left
i1= 59
write (x1,fmt) i1 ! converting integer to string using a 'internal file'
filename='output'//trim(x1)//'.dat'
! ====> filename: output00059.dat
Well here is a simple function which will return the left justified string version of an integer:
character(len=20) function str(k)
! "Convert an integer to string."
integer, intent(in) :: k
write (str, *) k
str = adjustl(str)
end function str
And here is a test code:
program x
integer :: i
do i=1, 100
open(11, file='Output'//trim(str(i))//'.txt')
write (11, *) i
close (11)
end do
end program x
I already showed this elsewhere on SO (How to use a variable in the format specifier statement? , not an exact duplicate IMHO), but I think it is worthwhile to place it here. It is possible to use the techniques from other answers for this question to make a simple function
function itoa(i) result(res)
character(:),allocatable :: res
integer,intent(in) :: i
character(range(i)+2) :: tmp
write(tmp,'(i0)') i
res = trim(tmp)
end function
which you can use after without worrying about trimming and left-adjusting and without writing to a temporary variable:
OPEN(1, FILE = 'Output'//itoa(i)//'.TXT')
It requires Fortran 2003 because of the allocatable string.
For a shorten version.
If all the indices are smaller than 10, then use the following:
do i=0,9
fid=100+i
fname='OUTPUT'//NCHAR(i+48) //'.txt'
open(fid, file=fname)
!....
end do
For a general version:
character(len=5) :: charI
do i = 0,100
fid = 100 + i
write(charI,"(A)"), i
fname ='OUTPUT' // trim(charI) // '.txt'
open(fid, file=fname)
end do
That's all.
I've tried #Alejandro and #user2361779 already but it gives me an unsatisfied result such as file 1.txt or file1 .txt instead of file1.txt. However i find the better solution:
...
integer :: i
character(len=5) :: char_i ! use your maximum expected len
character(len=32) :: filename
write(char_i, '(I5)') i ! convert integer to char
write(filename, '("path/to/file/", A, ".dat")') trim(adjustl(char_i))
...
Explanation:
e.g. set i = 10 and write(char_i, '(I5)') i
char_i gives " 10" ! this is original value of char_i
adjustl(char_i) gives "10 " ! adjust char_i to the left
trim(adjustl(char_i)) gives "10" ! adjust char_i to the left then remove blank space on the right
I think this is a simplest solution that give you a dynamical length filename without any legacy blank spaces from integer to string conversion process.
Try the following:
....
character(len=30) :: filename ! length depends on expected names
integer :: inuit
....
do i=1,n
write(filename,'("output",i0,".txt")') i
open(newunit=iunit,file=filename,...)
....
close(iunit)
enddo
....
Where "..." means other appropriate code for your purpose.
To convert an integer to a string:
integer :: i
character* :: s
if (i.LE.9) then
s=char(48+i)
else if (i.GE.10) then
s=char(48+(i/10))// char(48-10*(i/10)+i)
endif
Here is my subroutine approach to this problem. it transforms an integer in the range 0 : 9999 as a character. For example, the INTEGER 123 is transformed into the character 0123. hope it helps.
P.S. - sorry for the comments; they make sense in Romanian :P
subroutine nume_fisier (i,filename_tot)
implicit none
integer :: i
integer :: integer_zeci,rest_zeci,integer_sute,rest_sute,integer_mii,rest_mii
character(1) :: filename1,filename2,filename3,filename4
character(4) :: filename_tot
! Subrutina ce transforma un INTEGER de la 0 la 9999 in o serie de CARACTERE cu acelasi numar
! pentru a fi folosite in numerotarea si denumirea fisierelor de rezultate.
if(i<=9) then
filename1=char(48+0)
filename2=char(48+0)
filename3=char(48+0)
filename4=char(48+i)
elseif(i>=10.and.i<=99) then
integer_zeci=int(i/10)
rest_zeci=mod(i,10)
filename1=char(48+0)
filename2=char(48+0)
filename3=char(48+integer_zeci)
filename4=char(48+rest_zeci)
elseif(i>=100.and.i<=999) then
integer_sute=int(i/100)
rest_sute=mod(i,100)
integer_zeci=int(rest_sute/10)
rest_zeci=mod(rest_sute,10)
filename1=char(48+0)
filename2=char(48+integer_sute)
filename3=char(48+integer_zeci)
filename4=char(48+rest_zeci)
elseif(i>=1000.and.i<=9999) then
integer_mii=int(i/1000)
rest_mii=mod(i,1000)
integer_sute=int(rest_mii/100)
rest_sute=mod(rest_mii,100)
integer_zeci=int(rest_sute/10)
rest_zeci=mod(rest_sute,10)
filename1=char(48+integer_mii)
filename2=char(48+integer_sute)
filename3=char(48+integer_zeci)
filename4=char(48+rest_zeci)
endif
filename_tot=''//filename1//''//filename2//''//filename3//''//filename4//''
return
end subroutine nume_fisier
There is a file consisted of lines in the following format:
John, D E 100
Bob, F I 200
Oli, G H 1500
...
in general:
string, char char integer
The file needs to be read and stored in two arrays of which the first should store the string and characters1 and the second should store the integer.
How could this be done?
An initial attempt is made here.
1. Concatenated into single string as they are with the comma and the white spaces.
If the items in the input file are delimited by whitespaces or commas, we can read them using list-directed input (fmt=*), as suggested in the comments in the linked question, such that
read (funit, [fmt =] *, iostat = ios) surname, first_name, second_name, consumption
where the part "fmt =" is optional. The following is a slightly modified version of the original code in the linked page (please see comments in the code for more details):
module MyModule
implicit none !<-- this propagates to all routines in this module
contains
subroutine ReadFileIntoArrays (filename, name_arr, kWh_arr, ndata)
character(*), intent(in) :: filename !<-- an assumed-length string
character(*), intent(out) :: name_arr(:) !<-- an array of assumed-length strings
integer, intent(out) :: kWh_arr(:), ndata
character :: first_name, second_name !<-- a single char
character(50) :: surname !<-- a string of 50 chars
integer :: consumption, funit, ios, idx
funit = 10 ! use >= 10 (or open( newunit=funit, ... ) for recent compilers)
open (funit, file = filename, status = 'old')
idx = 0
do
read (funit, fmt = *, iostat = ios) & !<-- "&" means line continuation
surname, first_name, second_name, consumption
! "fmt = *" (list-directed input) tells the compiler
! to use whitespaces/commas as delimiters.
if (ios > 0) then
print *, "Wrong input format!" ; exit
else if (ios < 0) then
print *, "finished reading data." ; exit
else
idx = idx + 1
if (idx > size( name_arr )) stop "size of name_arr(:) too small"
! trim() chops trailing spaces in a string
name_arr( idx ) = trim(surname)//','//first_name//'.'//second_name//'.'
kWh_arr( idx ) = consumption
end if
end do
ndata = idx
close (funit)
end subroutine
end module
program MyMain
use MyModule
implicit none
integer :: consumption( 10 ), ndata, idx
character(50) :: names( 10 ) !<-- an array of size 10 (each element = 50-char string)
character(200) :: filename !<-- a string of 200 chars
filename = "clients.txt"
names = ""
consumption = 0
call ReadFileIntoArrays (filename, names, consumption, ndata)
print "(2a20)", "name", "consumption"
do idx = 1, ndata
print "(a20,i20)", trim( names( idx ) ), consumption( idx )
enddo
end program
Then, with the input in the question, the output becomes
finished reading data.
name consumption
John,D.E. 100
Bob,F.I. 200
Oli,G.H. 1500
Ok, so In my program I'm supposed to take in the name of the data file from the user than open it and read the contents. But when I open and read it all that characters just end up being ****** and all the integers end up being 0. IDK if it's how I'm reading in the file or the format?
The file will contain something like this: (where the number of cities is the first number)
4
SanDiego
0
350
900
1100
Phoenix
350
0
560
604
Denver
900
560
0
389
Dallas
1100
604
389
0
So far my code is this where first I take in the first number than on every firstnumber * I + I pass is supposed to go into the character array city. Now the rest of the numbers I am storing into a integer array, but really want it into a integer matrix called d_table but I couldn't think of a way to do that immediately on the read.
PROGRAM p4
IMPLICIT NONE
INTEGER :: number, status, I, J, K, permutation = 0, distance = 0, distance = 999999
CHARACTER(50) :: filename ! Filenames longer than 50 are truncated
CHARACTER(20), DIMENSION(10) :: city
INTEGER, DIMENSION(100) :: temp
INTEGER, DIMENSION(10,10) :: d_table
INTEGER, DIMENSION(10) :: path, best_path
WRITE (*, '(1x,A)', ADVANCE="NO") "Enter filename: "
READ *, filename
! Open the file we created and read the contents
OPEN(UNIT=15, FILE=filename, STATUS="OLD", ACTION="READ",&
IOSTAT=status)
IF(status /= 0) THEN
PRINT *, "ERROR, could not open file for reading."
STOP
END IF
READ (UNIT=15, FMT = 100, IOSTAT=status) number
J = 0
K = 0
DO I = 0, number*number
IF(I == J*number+J) THEN
READ (UNIT=15, FMT = 200, IOSTAT=status) city(J)
J = J + 1
ELSE
READ (UNIT=15, FMT = 100, IOSTAT=status) temp(K)
K = K + 1
END IF
END DO
K = 0
DO I = 0, number
DO J = 0, number
d_table(I,J) = temp(K)
K = K + 1
END DO
END DO
100 FORMAT(I6)
200 FORMAT (A)
END PROGRAM p4
This line
DO I = 0, number*number
looks wonky to me; the loop will be executed 17 times. Surely you want to read number groups of 5 lines, each group being one city name followed by four integers ? That would be a good case for a little loop nest, something like
do ix = 1, number
read(15,*) city(ix)
do jx = 1, 4
read(15,*) d_table(ix,jx)
end do
end do
Given such a simple input file format there's no need to bother with format statements, list-directed input will work just fine.
I can't see the point of all the index arithmetic the code is doing, perhaps I've missed something.
AM writing a code in FORTRAN to read a mesh file in STARCD format(has.cel, .vrt and .bnd) am trying to use a counter and a pointer array, to count the number of vertices and the cells the file has so that it would be easy to define the neighbours of the cells later. Can you please take a look at the code and correct me out please and i get an error Invalid form of array reference at (1).
ivrt(icell(i)%cell(2)%icounter(1)=ivrt(icell(i)%cell(2)%icounter(1)+1
1
The error is shown here for all the lines with the icounter. To give u the outline of what is being done, i have just declared the types i have used and the variables, am reading the three files .vrt(nov) .cel(noc) and .bnd(bnd) and defining the datastructure where .vrt has 4 columns, .cel has 5 columns.
program reader
implicit none
type::node
real,allocatable,dimension(:)::point1,icounter,vrt
end type
type::elemtype
integer,allocatable,dimension(:)::quad,refback
integer,allocatable,dimension(:)::tri
end type
type::cel
integer,allocatable,dimension(:)::cell,ishape,reffwd
end type
type::boundnode
integer,allocatable,dimension(:)::bnode
end type
type::boundarycell
integer,allocatable,dimension(:)::bcell
end type
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
type(node),allocatable,dimension(:)::ivrt
type(elemtype),allocatable,dimension(:)::iquad
type(elemtype),allocatable,dimension(:)::itri
type(cel),allocatable,dimension(:)::icell
type(boundnode),allocatable,dimension(:)::ibnode
type(boundarycell),allocatable,dimension(:)::ibcell
integer::nov,noc,bnd,nbnd,i,j,k,count1,count2,numquad,flag,numtri
integer::usercell,tcell,cindex
integer::celltype,n,m
integer,allocatable,dimension(:,:)::qedg1,qedg2,qedg3,qedg4
integer,allocatable,dimension(:,:)::tedg1,tedg2,tedg3,tedg4
integer,allocatable,dimension(:)::ctype
integer,allocatable,dimension(:)::quadvert
integer,allocatable,dimension(:)::trivert
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
CALL SYSTEM("wc STAR.vrt > NO_V")
OPEN (UNIT=10,FILE='NO_V',STATUS="OLD",ACTION="READ")
READ (10,"(i6)")nov
close(10)
print *, nov
CALL SYSTEM("wc STAR.cel > NO_C")
OPEN (UNIT=10,FILE='NO_C',STATUS="OLD",ACTION="READ")
READ (10,"(i6)")noc
close(10)
print *, noc
CALL SYSTEM("wc STAR.bnd > NO_B")
OPEN (UNIT=10,FILE='NO_B',STATUS="OLD",ACTION="READ")
READ (10,"(i6)")nbnd
close(10)
print *, nbnd
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
allocate(ivrt(nov))
do i=1,nov
allocate(ivrt(i)%vrt(1:4))
allocate(ivrt(i)%icounter(1))
end do
allocate(icell(noc))
do i=1,noc
allocate(ivrt(i)%icounter(1))
allocate(icell(i)%cell(1:5))
allocate(icell(i)%ishape(1))
allocate(icell(i)%reffwd(1))
end do
allocate(ibnode(nbnd))
do i=1,bnd
allocate(ibnode(i)%bnode(1:3))
end do
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
open(unit=10,file="STAR.vrt",status="old",action="read")
do i = 1,nov
read(10,*)ivrt(i)%vrt(1), ivrt(i)%vrt(2), ivrt(i)%vrt(3), ivrt(i)%vrt(4)
!print *, ivrt(i)%vrt(1), ivrt(i)%vrt(2), ivrt(i)%vrt(3), ivrt(i)%vrt(4)
end do
close (10)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
open(unit=10,file="STAR.cel",status="old",action="read")
do i=1,noc
read(10,*)icell(i)%cell(1),icell(i)%cell(2),icell(i)%cell(3),icell(i)%cell(4),icell(i)%cell(5)
ivrt(icell(i)%cell(2)%icounter(1)=ivrt(icell(i)%cell(2)%icounter(1)+1
ivrt(icell(i)%cell(3)%icounter(1)=ivrt(icell(i)%cell(2)%icounter(1)+1
ivrt(icell(i)%cell(4)%icounter(1)=ivrt(icell(i)%cell(2)%icounter(1)+1
if (icell(i)%cell(4).ne.icell(i)%cell(5))then
ivrt(icell(i)%cell(5)%icounter(1)=ivrt(icell(i)%cell(2)%icounter(1)+1
end if
end do
close(10)
do i=1,nov
allocate(ivrt(i)%point1(ivrt(i)%icounter(1)))
end do
ivrt(:)%icounter(:)=0
open(unit=10,file="STAR.cel",status="old",action="read")
do i=1,noc
read(10,*)icell(i)%cell(1),icell(i)%cell(2),icell(i)%cell(3),icell(i)%cell(4),icell(i)%cell(5)
ivrt(icell(i)%cell(2)%icounter(1)=ivrt(icell(i)%cell(2)%icounter(1)+1
ivrt(icell(i)%cell(3)%icounter(1)=ivrt(icell(i)%cell(2)%icounter(1)+1
ivrt(icell(i)%cell(4)%icounter(1)=ivrt(icell(i)%cell(2)%icounter(1)+1
ivrt(icell(i)%cell(2))%point1(ivrt(icell(i)%cell(2))%icounter(1))=i
ivrt(icell(i)%cell(3))%point1(ivrt(icell(i)%cell(2))%icounter(1))=i
ivrt(icell(i)%cell(4))%point1(ivrt(icell(i)%cell(2))%icounter(1))=i
if (icell(i)%cell(4).ne.icell(i)%cell(5))then
ivrt(icell(i)%cell(5))%point1(ivrt(icell(i)%cell(2))%icounter(1))=i
ivrt(icell(i)%cell(5))%icounter(1)=ivrt(icell(i)%cell(2))%icounter(1)+1
end if
end do
close(10)
There are two brackets missing... Do you mean:
ivrt( icell(i)%cell(2)%icounter(1) ) = ivrt( icell(i)%cell(2)%icounter(1)+1 )
^ ^
or:
ivrt( icell(i)%cell(2)%icounter(1) ) = ivrt( icell(i)%cell(2)%icounter(1) ) + 1
^ ^
That error is repeated several times throughout your code!
Like Alexander Vogt I agree that there are brackets missing. However, there is another problem.
In the defintion of the type cel the component cell is an integer array:
type :: cel
integer, allocatable, dimension(:) :: cell, ishape, reffwd
end type
type(cel), allocatable, dimension(:) :: icell
so one cannot have a reference icell(i)%cell(2)%...
Perhaps, then, the declaration of cel should be
type :: cel
type(node), allocatable, dimension(:) :: cell
! Also components ishape, reffwd of some type.
end type
end type
And then the bracket corrections additionally.
I have an issue. I have values in a workbook that are getting read into an array. The values come from an XML list so sometimes can be numbers or text, and if they are numbers in text format ("1" for example), they need to be converted to number format, so I multiply them by 1. If the value is text "LS" for example, I am trying to use an error handler to keep the value as "LS".
I have developed the code below: It works once, but the next time I use a similar method (with 'Dummy2') the code it produces a 'Type Mismatch' error.
On Error GoTo Dummy1
For i = 1 To nrows1 - 1
If (Worksheets("Import").Cells(i + 1, 26) * 1 = "") Then
Dummy1:
Table1(i, 1) = Worksheets("Import").Cells(i + 1, 26)
Else
Table1(i, 1) = Worksheets("Import").Cells(i + 1, 26) * 1
End If
Next
On Error GoTo 0
I also tried Clearing Err after the above code with no success.
Please help!
You can use the IsNumeric function:
Sub test3()
Dim Test As Variant
Dim i As Long
For i = 1 To nrows1 - 1
Test = Worksheets("Import").Cells(i + 1, 26)
If IsNumeric(Test) Then
Table1(i, 1) = Test * 1
Else
Table1(i, 1) = Test
End If
Next
End Sub
use VAL(Worksheets("Import").Cells(i + 1, 26)) to determine if it is a Number or not. VAL will give you 0 for "LS".
You could try TryParse functions - but not sure if its available in your version of VBA