I'm writing in fortran (90). My program must read file1, do something with every line of it and write result to file2. But the problem - file1 has some unneeded information in first line.
How can I skip a line from input file using Fortran?
The code:
open (18, file='m3dv.dat')
open (19, file='m3dv2.dat')
do
read(18,*) x
tmp = sqrt(x**2 + 1)
write(19, *) tmp
end do
First line is a combination of text and numbers.
One possible solution has already been presented to you which uses a "dummy variable", but I just wanted to add that you don't even need a dummy variable, just a blank read statement before entering the loop is enough:
open(18, file='m3dv.dat')
read(18,*)
do
...
The other answers are correct but this can improve conciseness and (thus) readability of your code.
Perform a read operation before the do loop that reads whatever is on the first line into a "dummy" variable.
program linereadtest
implicit none
character (LEN=75) ::firstline
integer :: temp,n
!
!
!
open(18,file='linereadtest.txt')
read(18,*) firstline
do n=1,4
read(18,'(i3)') temp
write(*,*) temp
end do
stop
end program linereadtest
Datafile:
This is a test of 1000 things that 10
of which do not exist
50
100
34
566
!ignore the space in between the line and the numbers, I can't get it to format
open (18, file='m3dv.dat')
open (19, file='m3dv2.dat')
read(18,*) x // <---
do
read(18,*) x
tmp = sqrt(x**2 + 1)
write(19, *) tmp
end do
The line added just reads the first line and then overwrites it with the seconde on the first iteration.
Related
How can I get specific lines in a file and add it to array?
For example: I want to get lines 200-300 and put them inside an array. And while at that count the total line in the file. The file can be quite big.
File.each_line is a good reference for this:
lines = [] of String
index = 0
range = 200..300
File.each_line(file, chomp: true) do |line|
index += 1
if range.includes?(index)
lines << line
end
end
Now lines holds the lines in range and index is the number of total lines in the file.
To prevent reading the entire file and allocating a new array for all of its content, you can use File.each_line iterator:
lines = [] of String
File.each_line(file, chomp: true).with_index(1) do |line, idx|
case idx
when 1...200 then next # ommit lines before line 200 (note exclusive range)
when 200..300 then lines << line # collect lines 200-300
else break # early break, to be efficient
end
end
I have a while loop that generally works well but I would like to convert it to a for loop. The while loop is as follows:
with open(filename) as a:
while tally != c:
line = a.readline()
c = line.strip()
Where I open a file, read each line until I find the line I am interested in. The for loop is as follows:
with open(filename) as a:
for line in enumerate (a):
if tally == c:
break
elif tally != c:
c = line
It appears that the loop just reads to the end of the file every time and turns the variable c into a tuple consisting of a random number and the last line of the file. I know the string contained in tally exists in the file because the while loop performs the intended function.
Fixed code:
with open(filename) as a:
for line in a:
if tally == c:
break
else:
c = line.strip()
Two issues:
for line in enumerate(a) means line gets a tuple on each iteration of the line number and the actual line from the file. Read up on enumerate to understand its use.
c = line is different from c = line.strip() in your original loop.
Instead of c=line it must be c=line.strip().
I am trying to put an input string into sub-string arrays. The number of data in the input file are less than 10 but unknown. The number of spaces between each data is also unclear.
Example:
Asd B Cwqe21 Ddsw Eww
I am quite novice to Fortran, so I do not know which format I should use. My problem is that I do not know the number of data (here I assumed that there are 5), so how can I make the code work?
I tried the following which did not work:
CHARACTER (LEN=100), DIMENSION(10) :: string
READ (1,*) (string,I=1,10)
It seems that the error I got was because there was no 6th string to read and put into string(6).
I tried using the "Index" to find the space, but since I do not know how many spaces are in the string, it did not help me.
I don't know if this is more or less elegant/efficient than the standard approach in M.S.B's comment, but an interesting alternative.
integer istart,nw
character (len=100) line,wd,words(100)
open(1,file='t.dat')
read(1,'(a)')line
istart=1
nw=0
do while(len(trim(line(istart:))).gt.0)
read(line(istart:),*)wd
istart=istart+index(line(istart:),trim(wd))+len(trim(wd))
nw=nw+1
words(nw)=trim(wd)
enddo
write(*,*)trim(line)
write(*,*)('/',trim(words(k)),k=1,nw),'/'
end
An inefficient approach that is simple to program is to try to read the maximum number of items, and if this fails to successively try to read one fewer items until the read is successful, as shown below:
program xread_strings
integer, parameter :: nw = 10
character (len=1000) :: text
character (len=20) :: words(nw)
integer :: i,ierr,nread
text = "Asd B Cwqe21 Ddsw Eww"
nread = 0
do i=nw,1,-1
read (text,*,iostat=ierr) words(:i)
if (ierr == 0) then
nread = i
exit
end if
end do
if (nread > 0) write (*,*) "read ",nread," words: ",("'"//trim(words(i)) // "' ",i=1,nread)
end program xread_strings
! g95 Output:
! read 5 words: 'Asd' 'B' 'Cwqe21' 'Ddsw' 'Eww'
I have a text file in which I want to replace my temperature values. The position of this numbers are random in the text and looks like:
'xdd "D:\Data\Ioana\DH1_Short_slow\DH1_360_2.xye" xye_format
local !Temperature 360
... (more text)
prm bnonh360 1.66237`_0.41541
prm bh360 = 1.2 * bnonh360;
site Na1 x ref_flag -0.11868`_0.00258 y ref_flag 0.51229`_0.00446 z ref_flag 0.00330`_0.00107 occ Na 1 beq = bnonh360;
...(more text)
Out_CIF_STR_Uiso("D:\Data\Ioana\DH1_Short_slow\DH1_360.cif")
Out_Profile("D:\Data\Ioana\DH1_Short_slow\DH1_360_plot.pro")
Out_Tick("D:\Data\Ioana\DH1_Short_slow\DH1_360_plot.tic") '
More text means more text in the file. I want to replace 360 with 365 for instance.
I have tried something like
do
read(10,'(a)',iostat=iok) line1
found = ( (index(line1,'360') /=0) )
if (found) then
write(*,*) '365'
endif
if(line1 == '$') exit
write(*,*) line1
write(40,*,iostat=iok) line1
enddo
But this just writes 365 in a next line where 360 is found.
Thanks
you are discarding valuable information in the return value of index intrinsic. to replace '360' to '365', which are of equal length, you can do
pos=index(line1,'360')
if(pos>0) line1(pos:pos+2)='365'
or in general if you want to replace first occurrence of substring s1 with s2 in a string line, with s1 and s2 possibly having different length, and that they are padded with an indefinite number of spaces that you did not want to include in the replacement, you do
pos=index(line,trim(s1))
if(pos>0) line=line(1:pos-1)//trim(s2)//line(pos+len_trim(s1):len_trim(line))
Should you want to include padding space in the replacement you can change the position offsets in the RHS substrings. If you want to perform replacements for all occurrences you can enclose this in a while loop
This is of course under the assumption that the declared length of line is long enough to hold the new string. However, note that self-assignment is prohibited in FORTRAN77 so if that is what you are using, you will need different string variable to store the result.
I am trying to read a file that looks as follows:
Data Sampling Rate: 256 Hz
*************************
Channels in EDF Files:
**********************
Channel 1: FP1-F7
Channel 2: F7-T7
Channel 3: T7-P7
Channel 4: P7-O1
File Name: chb01_02.edf
File Start Time: 12:42:57
File End Time: 13:42:57
Number of Seizures in File: 0
File Name: chb01_03.edf
File Start Time: 13:43:04
File End Time: 14:43:04
Number of Seizures in File: 1
Seizure Start Time: 2996 seconds
Seizure End Time: 3036 seconds
So far I have this code:
fid1= fopen('chb01-summary.txt')
data=struct('id',{},'stime',{},'etime',{},'seizenum',{},'sseize',{},'eseize',{});
if fid1 ==-1
error('File cannot be opened ')
end
tline= fgetl(fid1);
while ischar(tline)
i=1;
disp(tline);
end
I want to use regexp to find the expressions and so I did:
line1 = '(.*\d{2} (\.edf)'
data{1} = regexp(tline, line1);
tline=fgetl(fid1);
time = '^Time: .*\d{2]}: \d{2} :\d{2}' ;
data{2}= regexp(tline,time);
tline=getl(fid1);
seizure = '^File: .*\d';
data{4}= regexp(tline,seizure);
if data{4}>0
stime = '^Time: .*\d{5}';
tline=getl(fid1);
data{5}= regexp(tline,seizure);
tline= getl(fid1);
data{6}= regexp(tline,seizure);
end
I tried using a loop to find the line at which file name starts with:
for (firstline<1) || (firstline>1 )
firstline= strfind(tline, 'File Name')
tline=fgetl(fid1);
end
and now I'm stumped.
Suppose that I am at the line at which the information is there, how do I store the information with regexp? I got an empty array for data after running the code once...
Thanks in advance.
I find it the easiest to read the lines into a cell array first using textscan:
%// Read lines as strings
fid = fopen('input.txt', 'r');
C = textscan(fid, '%s', 'Delimiter', '\n');
fclose(fid);
and then apply regexp on it to do the rest of the manipulations:
%// Parse field names and values
C = regexp(C{:}, '^\s*([^:]+)\s*:\s*(.+)\s*', 'tokens');
C = [C{:}]; %// Flatten the cell array
C = reshape([C{:}], 2, []); %// Reshape into name-value pairs
Now you have a cell array C of field names and their corresponding (string) values, and all you have to do is plug it into struct in the correct syntax (using a comma-separated list in this case). Note that the field names have spaces in them, so this needs to be taken care of before they can be used (e.g replace them with underscores):
C(1, :) = strrep(C(1, :), ' ', '_'); %// Replace spaces with underscores
data = struct(C{:});
Here's what I get for your input file:
data =
Data_Sampling_Rate: '256 Hz'
Channel_1: 'FP1-F7'
Channel_2: 'F7-T7'
Channel_3: 'T7-P7'
Channel_4: 'P7-O1'
File_Name: 'chb01_03.edf'
File_Start_Time: '13:43:04'
File_End_Time: '14:43:04'
Number_of_Seizures_in_File: '1'
Seizure_Start_Time: '2996 seconds'
Seizure_End_Time: '3036 seconds'
Of course, it is possible to prettify it even more by converting all relevant numbers to numerical values, grouping the 'channel' fields together and such, but I'll leave this to you. Good luck!