Write array to file by columns

风流意气都作罢 提交于 2020-01-21 16:43:12

问题


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:

  1. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!