在写入文件

时间:2015-11-11 20:52:47

标签: fortran

我有一个连接多个文件的Fortran例程。给定输入文件:

f1.txt

1  1 2 3
2  1 2 3
3  1 2 3

f2.txt

1  4 5 6 7
2  4 5 6 7
3  4 5 6 7

输出

1  1 2 3 4 5 6 7
2  1 2 3 4 5 6 7
3  1 2 3 4 5 6 7

没什么大不了的,很简单。应该接受任意数量的文件,在忽略第一列的同时对它们进行行连接。每个文件的列数可能不同,但行数可能不同。列包含数值,而不仅仅是整数。

问题

由于某种原因,当列数不同时,新行会在确定性位置潜入。在我可以确定的情况下,它发生在"切换"到列数较少的文件。它不应该发生(换行),但确实如此。为什么呢?

PROGRAM TEST
IMPLICIT NONE

character(255), dimension(2) :: fns
integer :: files, nlines, result
character(255) :: fnout
integer, dimension(2) :: ncols

! subroutine rowconcat(files, fns, fnout, nlines, ncols, result)

files=2
fns(1) = 'f2.txt'
fns(2) = 'f1.txt'
fnout = 'fres.txt'
nlines=3
ncols(1) = 2
ncols(2) = 4



CALL rowconcat(files, fns, fnout, nlines, ncols, result)  ! In rowconcat.f95

END

! Concatenates genotype matrices (from e.g. multiple chromosomes) into one.
! No row ID checking.
subroutine rowconcat(files, fns, fnout, nlines, ncols, result)
  implicit none

  integer, intent(in) :: files, nlines
  character(255), intent(in) :: fnout
  character(255), dimension(files), intent(in) :: fns
  integer, dimension(files), intent(in) :: ncols
  integer, intent(out) :: result

  integer :: i, j, id, stat
  integer, dimension(files) :: units
  real, dimension(:,:), allocatable :: row
  character(50) :: fmt0
  character(50), dimension(files) :: fmt
  character(4), dimension(files) :: advance

  allocate(row(files,maxval(ncols, 1)))
  !row(:) = 9 

  advance(:) = 'no'
  advance(files) = 'yes'

  print *, files, nlines
  print *, advance
  do i=1,files
    print *, trim(fns(i)), ncols(i)
  enddo
  !print *,nlines,ncols

  do i=1,files
    units(i) = 200 + i
    open(units(i), file=fns(i), status='OLD')
    write(fmt0, '(i5)') ncols(i)
    fmt(i)='('//trim(adjustl(fmt0))//'F5.2)'
  end do
  open(55, file=fnout, status='UNKNOWN')
  print *, fmt

  do j=1,nlines
    do i=1,files
      read(units(i), *, iostat=stat) id, row(i,1:ncols(i))
      if (stat /= 0) exit
      !print *, ncols(i), row(1:ncols(i)), 'Bla.'
      if (i == 1) write(55, '(i20)', advance='no') id
      write(55, fmt, advance=advance(i)) row(i,1:ncols(i))
    end do
    if (stat /= 0) exit
  end do

  deallocate(row)

  close(55)
  do i=1,files
    close(units(i))
  end do

  result=stat

end subroutine rowconcat

(子程序是R包的一部分,所以有一些包装函数,但上面的代码再现了这个问题。)

结果

                   1 1.00 2.00 3.00 4.00 5.00 6.00
 7.00
                   2 1.00 2.00 3.00 4.00 5.00 6.00
 7.00
                   3 1.00 2.00 3.00 4.00 5.00 6.00
 7.00

不,它不是自己包装的。新线已经在最后一个元素之前潜入。为什么呢?

交换两个文件,例程按预期工作。但是,它在某种程度上取决于列数的顺序。

1 个答案:

答案 0 :(得分:2)

如评论中所述,您的代码不会重现您的输出。您的初始设置与输入文件不一致。

files=2
fns(1) = 'f2.txt'
fns(2) = 'f1.txt'
fnout = 'fres.txt'
nlines=3
ncols(1) = 2
ncols(2) = 4

应该是

files=2
fns(1) = 'f1.txt'
fns(2) = 'f2.txt'
fnout = 'fres.txt'
nlines=3
ncols(1) = 3
ncols(2) = 4

在你的子程序中,你有一些混乱的代码和很多逻辑来管理你的读写。正如@AlexanderVogt所说,读入数组并立即写出来更有意义。我会中途遇见你,而不是基于行的IO,以防内存限制你一次将所有数据加载到内存中的能力。考虑rowconcat的这个修改版本:

subroutine rowconcat(files, fns, fnout, nlines, ncols, result)
  implicit none

  integer, intent(in) :: files, nlines
  character(255), intent(in) :: fnout
  character(255), dimension(files), intent(in) :: fns
  integer, dimension(files), intent(in) :: ncols
  integer, intent(out) :: result

  integer :: i, j, id, stat, total_columns, start_column
  integer, dimension(files) :: units
  real, dimension(:), allocatable :: row
  character(50) :: fmt
  character(8) :: str_cols

  total_columns = sum(ncols)
  allocate(row(total_columns))

  do i=1,files
    units(i) = 200 + i
    open(units(i), file=fns(i), status='OLD')
  end do
  open(55, file=fnout, status='UNKNOWN')
  write(str_cols,'(i8)') total_columns
  write(fmt,'(A)') '(I20,'//trim(adjustl(str_cols))//'F5.2)'

  do j=1,nlines
     start_column = 1
     do i=1,files
        read(units(i), *, iostat=stat) id, row(start_column:start_column+ncols(i)-1)
        start_column = start_column+ncols(i)
        if (stat /= 0) exit
     end do
     write(55, fmt, iostat=stat) id, row
     if (stat /= 0) exit
  end do

  deallocate(row)

  close(55)
  do i=1,files
    close(units(i))
  end do

  result=stat

end subroutine rowconcat

对此的主要更改是

  • row
  • 中分配整个输出行元素
  • 对所有写入使用单一格式fmt
  • 每行
    • 对于每个文件:从文件读取元素到正确位置的行
    • 输出行 这会减少管家逻辑,只需记下您所在的列,即可开始填写下一个文件行。

我没有理会为什么你的代码无法正常工作,因为从表面上看它产生了胡言乱语。直到我将子程序修改为我的版本时我才注意到你的输入值被破坏了,在那一点上我没有心情恢复工作代码并重新开始试图看看你做错了什么。