 |
forums.silverfrost.com Welcome to the Silverfrost forums
|
View previous topic :: View next topic |
Author |
Message |
simon
Joined: 05 Jul 2006 Posts: 299
|
Posted: Wed May 01, 2013 2:09 pm Post subject: BACKSPACE |
|
|
Imagine the following trivial dataset:
I only want to read the numbers, but I need to read them in unformatted, and the leading text is of a consistent length, but is not a consistent number of words. I may not always want to read the first line.
Consider the following program to read the data. It skips line 1, and then reads the entire line to work out how long the leading string is (not shown). It then attempts to backspace to read line 2 again, but this time to skip over the text and then read in the data.
Code: | PROGRAM p
REAL :: d
CHARACTER(LEN=64) :: c
!
OPEN (UNIT=11,FILE='external_file.txt',ACTION='read',FORM='formatted',STATUS='old')
READ (UNIT=11,FMT=*)
READ (UNIT=11,FMT='(A)') c
! ... some calculations to determine that '(A3)' will be adequate for skipping the leading string
BACKSPACE (UNIT=11)
READ (UNIT=11,FMT='(A3)',ADVANCE='no') c
READ (UNIT=11,FMT= *) d
PRINT *, d
END PROGRAM p
|
This all works great, except if the line used to diagnose the length of the string is the last line in the file. If it is, using BACKSPACE works as long as the last line ends with a carriage return, but if it does not then BACKSPACE takes you a line earlier. Try reading the data at the top of this post in these two ways. Without a carriage return at the end of line 2, the program reads in line 1; with a carriage return it reads in line 2.
As far as I can tell, that's how it should be (FTN95 is doing what the standard says), but it would still be nice to know where I am in the file after invoking BACKSPACE. IOSTAT returns a zero in both cases. There are of course work-arounds such as reading the data from c rather than
backspacing at all, but I don't even know whether c has been long enough to read in the entire line of data. I could rewind and get back to this point again, but it may have taken a long time to find this location.
Anybody have any good ideas?[/code] |
|
Back to top |
|
 |
brucebowler Guest
|
Posted: Wed May 01, 2013 6:56 pm Post subject: |
|
|
Well to start, I'd read each line into a string and then work on that string with internal reads (and friends) and eliminate the use of backspace all together.
Cleaner (IMHO) and probably much faster... |
|
Back to top |
|
 |
davidb
Joined: 17 Jul 2009 Posts: 560 Location: UK
|
Posted: Wed May 01, 2013 10:55 pm Post subject: |
|
|
This looks like a bug in FTN95.
When the last line of the file doesn't have a carriage return, this record can still be read. After this read, the "current record" should then be the End-of-file record.
A subsequent use of BACKSPACE should then position to the last line again.
On the NAG compiler your program gives 2.0 whether or not there is a carriage return or not on the last line.
With FTN95 I get 2.0 if there is a carriage return and 1.0 if there isn't. _________________ Programmer in: Fortran 77/95/2003/2008, C, C++ (& OpenMP), java, Python, Perl |
|
Back to top |
|
 |
davidb
Joined: 17 Jul 2009 Posts: 560 Location: UK
|
Posted: Wed May 01, 2013 10:57 pm Post subject: Re: |
|
|
brucebowler wrote: | Well to start, I'd read each line into a string and then work on that string with internal reads (and friends) and eliminate the use of backspace all together. |
Are list-directed reads of internal files (characters) allowed? _________________ Programmer in: Fortran 77/95/2003/2008, C, C++ (& OpenMP), java, Python, Perl |
|
Back to top |
|
 |
simon
Joined: 05 Jul 2006 Posts: 299
|
Posted: Thu May 02, 2013 4:40 am Post subject: |
|
|
Quote: | Well to start, I'd read each line into a string and then work on that string with internal reads (and friends) and eliminate the use of backspace all together. |
I normally would, but the files may be massive. I only have to do one backspace, essentially to identify in what column the data start, and then I can read the rest of the file. If I had to read in every line as a string and then read the string, I would assume (but have not tested) that that would be less efficient. The other reason for avoiding reading in each line as a string is that I would have to impose a limit on the width of the file. In practice the limit may not be very restrictive if I define a large enough string, but in general I try to avoid imposing any limits other than those of the compiler.
Am interested to find out if David's assessment that this is a compiler bug is true ... |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Fri May 03, 2013 2:54 am Post subject: |
|
|
I would expect that doing a double read would have a minimal performance cost. Try it and you will be pleasently surprised.
The double read approach could be:
Code: | PROGRAM p
REAL :: d
CHARACTER(LEN=64) :: c
integer :: n, i, iostat, l
!
OPEN (UNIT=11,FILE='external_file.txt',ACTION='read',FORM='formatted',STATUS='old')
!
!
do i = 1, huge(i)
READ (UNIT=11,FMT='(A)', iostat=iostat) c
if (iostat/= 0) then
write (*,*) 'file read error : iostat=',iostat
exit
end if
! ... some calculations to determine that '(A3)' will be adequate for skipping the leading string
n = 4
l = len_trim (c)
if (l < n) then
write (*,*) 'no data in line',i
cycle
end if
!
READ (c(n:), FMT=*, iostat=iostat) d
PRINT *, i, d, l, iostat
end do
!
end |
Code: | ! external_file.txt
aaa 1
abc 2
xxx 3
zz 4 |
Not sure of what happens if FMT=* and c(4:) is blank, ie l<4
You might want to enhance your test for n and l
If you need to get n from the last line only, then you could read the file, storing the last valid line for each read, then rewind and read all the file ?
Faster would be to store all lines in a character array,
ie character c(num_lines)*64
read (c(line_number)(n:), fmt=*, iostat=iostat) d ! should work
Not sure if you are saying you need all lines of data or only the last line ?
Using an internal read of "c" is definately better than a backspace, especially for a large file. The array c can have nearly 33,554,432 (2^25) lines if allocatable.
John |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Fri May 03, 2013 3:40 am Post subject: |
|
|
This example demonstrates storing the file as 1 pass read.
Alternatively you could do a 2-pass read to determine the number of lines, or
a single pass read to store only the last line
John
Code: | PROGRAM p
REAL :: d
CHARACTER (LEN=64), allocatable, dimension(:) :: c
CHARACTER (LEN=64) :: last_c
integer :: n, i, iostat, l, last_line, max_lines
!
OPEN (UNIT=11,FILE='external_file.txt',ACTION='read',FORM='formatted',STATUS='old')
!
max_lines =30000000
allocate ( c(max_lines) )
write (*,*) 'c has',size (c) * 64,' bytes'
!
do i = 1, max_lines
READ (UNIT=11,FMT='(A)', iostat=iostat) c(i)
if (iostat/= 0) then
write (*,*) 'file read error at line',i,' : iostat=',iostat
exit
end if
l = len_trim (c(i))
if (l > 3) then
last_line = i
last_c = c(i)
else
write (*,*) 'no data in line',i
end if
end do
!
! ... some calculations to determine that '(A3)' will be adequate for skipping the leading string
n = 4
l = len_trim (c(last_line))
!
READ (c(last_line)(n:), FMT=*, iostat=iostat) d
PRINT *, last_line, d, l, i, iostat
!
end |
|
|
Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|