问题
Is there a way to write columns to a file one by one? For example, I would like to write:
write(1,*) 1
write(1,*) 2
And then write (possibly in another subroutine)
write(1,*) 3
write(1,*) 4
in such a way that will produce an output file in the format:
1 3
2 4
without combining the arrays (e.g.)
write(1,*) 1,3
write(1,*) 2,4
I'm thinking that there may be a way to move the "pointer" (of file location) back to the beginning and add spaces or something, but I really have no idea if this is possible. Any help is greatly appreciated!
So far, this is my attempt to make a subroutine to make this work:
subroutine writeArrayToNthColumn(arr,u,n)
implicit none
real(dpn),dimension(:),intent(in),target :: arr
real(dpn),dimension(:),pointer :: parr
integer,intent(in) :: u,n
integer :: i,s
s = size(arr)
allocate(parr(s))
parr = arr
rewind(u)
if (n.eq.1) then
do i=1,s
write(u,'(1F20.10)') parr(i)
enddo
else
do i=1,s
write(u,'(1F40.10)') parr(i)
enddo
endif
end subroutine
But upon calling the subroutine a second time, the first column is deleted..
回答1:
The comments above from George, and High Performance Mark are exactly right. I'm posting this as an answer, because it has source code, but it should be considered an extended comment, whose only purpose is to demonstrate how right they are.
You can do this, in a sense, although under no circumstances should you do so. Moving around within a file - a so called seek - is an incredibly expensive operation. On a local hard drive, this can take 10-20 milliseconds or so per operation (eg, in this case, per entry); if you are using a large shared system it can be much larger than that.
Moving things around in memory, however, is much less expensive. A well-tuned transpose function will do most of the moving around in cache, with operation that take a few nanoseconds; the data will get batched into and from main memory in fewer operations which each take ~100ns or so. Even a seek on a modern SSD will take something like dozens of microseconds.
In other words, there is no situation in which doing the transpose in your file I/O will be less than hundreds of times slower than doing the transpose in memory (and then back if needed). You still have to do the file/IO in the end, but if you are writing out in one batch, this is much faster.
So let's try some examples in Fortran, since that's the language of this question. Fortran, sensibly, makes this somewhat harder by making it difficult to access file locations directly, and even to explicitly output in carriage returns; I've used direct-access IO here and hard-coded in a non-portable way of doing the carriage returns.
Here's doing the transpose in your I/O:
program badiotxt
implicit none
integer, parameter :: asize = 200
integer, dimension(asize, asize) :: a
integer :: i, j
integer :: record
forall (i=1:asize, j=1:asize)
a(i,j) = (i-1)*asize+j
end forall
open(unit=7,file="bad.txt", status="new", access="direct", &
form="formatted", action="write", recl=10)
do j=1,asize
do i=1,asize-1
record=(j-1)*asize+i
write(7, rec=record, fmt="(2X,I7,1X)") a(i,j)
enddo
enddo
i = asize
do j=1,asize
record=(j-1)*asize+i
write(7, rec=record, fmt="(1X,I7,A1,A1)") a(i,j), char(13), char(10)
enddo
close(7)
end program badiotxt
and here's one done by transposing the array:
program goodiotxt
implicit none
integer, parameter :: asize = 200
integer, dimension(asize, asize) :: a
integer :: i, j
integer :: record
forall (i=1:asize, j=1:asize)
a(i,j) = (i-1)*asize+j
end forall
open(unit=7,file="good.txt", status="new", &
form="formatted", action="write")
a = transpose(a)
do i=1,asize
write(7,fmt="(1X,200(2X,I7))") (a(i,j), j=1,asize)
enddo
a = transpose(a)
close(7)
end program goodiotxt
Note that the good version is cleaner code, and that these are not large arrays by any stretch. The resulting times are:
- Transpose in memory: 0.05s
- Transpose on disk: 1.70s
- Slowdown: 34x
回答2:
I've a bit more time now than when I first commented on this question. This is, however, an extended comment rather than a full-blown answer.
Even IF (that's a big if) I had to write a data file column-by-column I wouldn't use direct access IO as @Jonathan Dursi's answer proposes. That (direct access IO) is for situations where it is difficult to predict, at the time the code is written, the ordering of writes to a file. In OP's case the ordering of writes seems to be entirely predictable.
I would:
- Write column 1, element-by-element, to a file.
2.1 Open a second file for writing, and the first file for reading.
2.2 Read the first line from the first file, write it to the second file and append the first element of the next column.
2.3 Repeat 2.2 until I got to the end of the column/file.
2.4 Close the second file (which now contains columns 1 and 2) and the first file.
3.1 Open the second file for reading, and perform a destructive open on the first file for writing.
... by now you should have got the picture.
来源:https://stackoverflow.com/questions/23834138/write-array-to-file-by-columns