我有一个连接多个文件的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
不,它不是自己包装的。新线已经在最后一个元素之前潜入。为什么呢?
交换两个文件,例程按预期工作。但是,它在某种程度上取决于列数的顺序。
答案 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
我没有理会为什么你的代码无法正常工作,因为从表面上看它产生了胡言乱语。直到我将子程序修改为我的版本时我才注意到你的输入值被破坏了,在那一点上我没有心情恢复工作代码并重新开始试图看看你做错了什么。