【问题标题】:Reading a multi-dimensional array of unknown shape读取未知形状的多维数组
【发布时间】:2014-06-12 08:27:48
【问题描述】:

我想从文件中获取数据,该文件的数据内容可以具有可变大小。但是,结构非常简单。 3 列和未定义的行数。我认为使用可分配的多维数组和显式 DO 循环可以解决我的问题。到目前为止,这是我的代码

program arraycall
    implicit none

    integer, dimension(:,:), allocatable :: array
    integer :: max_rows, max_cols, row, col

    allocate(array(row,col))

    open(10, file='boundary.txt', access='sequential', status='old', FORM='FORMATTED')

     DO row = 1, max_rows
       DO col = 1, max_cols
            READ (10,*) array (row, col)
       END DO
     END DO

     print *, array (row,col)

     deallocate(array)

 end program arraycall 

现在我面临的问题是我不知道应该如何定义这些 max_rows 和 max_cols,这与它的大小未知这一事实产生了共鸣。

示例文件可能看起来像

11 12 13

21 22 23

31 32 33

41 42 43

所以我想出了动态(动态)估计文件记录长度的方法。更新以供其他人参考

!---------------------------------------------------------------------
! Estimate the number of records in the inputfile
!---------------------------------------------------------------------
    open(lin,file=inputfile,status='old',action='read',position='rewind')

    loop1: do
      read(lin,*,iostat=eastat) inputline
      if (eastat < 0) then
        write(*,*) trim(inputfile),": number of records = ", numvalues
        exit loop1
      else if (eastat > 0 ) then
        stop "IO-Error!"
      end if

      numvalues=numvalues+1
    end do loop1
!-----------------------------------------------------------------------
! Read the records from the inputfile
!-----------------------------------------------------------------------
    rewind(lin)
    allocate (lon(numvalues),lat(numvalues),value(numvalues))

    do i=1,numvalues
      read(lin,*) lon(i),lat(i),value(i)
    end do

    close(lin)

【问题讨论】:

    标签: arrays fortran fortran90 dynamic-arrays


    【解决方案1】:

    我认为您有 3 个选项,其中两个已经描述过:

    1. 读取文件两次。首先读取行数,然后分配并读取值。如您所说,如果 I/O 时间相关,则效率低下。

    2. 正如@AlexanderVogt 建议的那样,估计最大行数。您不需要在所有代码中携带这个大矩阵。您可以定义第二个数组并执行类似的操作(基于@AlexanderVogt 代码):

      allocate(array2(3,tot_rows))
      array2 = array(:, :tot_rows)
      deallocate(array)
      

      不幸的是,恐怕您需要 2 个不同的数组,因为您无法就地调整大小。这也意味着,在短时间内,如果 arrayarrray2 很大,您将使用大量内存。

    3. 使用链表。这是最优雅的解决方案,允许您只读取一次文件,而无需预先分配数组。但它是最难编码的。这是一个适用于其中一个数组的简单示例。您需要三个链表或一个链表:

      integer, dimension(3) :: data
      

      如果您希望它使用 3 列。

    链表代码:

    program LinkedList
    implicit none
      integer :: i, eastat, value, numvalues
      type node
          integer :: data
          type( node ), pointer :: next
      end type node
      integer, dimension(:), allocatable :: lon
      type( node ), pointer :: head, current, previous
    
    
      nullify( head )   ! Initialize list to point to no target.
    
      open(10,file='data.dat',status='old',action='read', position='rewind')
      numvalues = 0 
      do 
          read(10,*,iostat=eastat) value
          if (eastat < 0) then
              write(*,*) "number of records = ", numvalues
              exit
          else if (eastat > 0 ) then
              stop "IO-Error!"
          end if
          allocate( current )
          current%data = value
          current%next => head
          head => current
          numvalues=numvalues+1
      end do 
      close(10)
    ! The list is read. You can now convert it into an array, if needed for
    ! numerical efficiency
    
      allocate(lon(numvalues))  
      current => head
      ! You could transverse the list this way if you hadn't kept numvalues
      !do  while ( associated( current ) )
      do i= numvalues, 1, -1
          lon(i) = current%data
          previous => current
          current => current%next
    !       head => current
          deallocate(previous)
       end do
    
    
    ! Output the list, deallocating them after use.
    print *,"lon = ", lon
    
    end program LinkedList
    

    【讨论】:

      【解决方案2】:

      您可以定义最大允许行数并使用iostat 来检查文件的结尾(或错误):

      program arraycall 
      
        implicit none
        integer, dimension(:,:), allocatable :: array
        integer           :: row
        integer           :: stat ! Check return values
        ! Define max. values
        integer,parameter :: max_rows=1000
        integer,parameter :: max_cols=3    ! As stated in the question
        ! Total number of rows in the file
        integer           :: tot_rows
      
        allocate( array(max_cols,max_rows), stat=stat)
        ! Always a good idea to check the return value
        if ( stat /= 0 ) stop 'Cannot allocate memory!'
      
        open(10, file='boundary.txt', access='sequential', &
             status='old', FORM='FORMATTED')
      
        DO row = 1, max_rows
          ! You can directly read in arrays! 
          READ (10,*,iostat=stat) array(:,row)
          if ( stat > 0 ) then
            stop 'An error occured while reading the file'
          elseif ( stat < 0 ) then
            tot_rows = row-1
            print *, 'EOF reached. Found a total of ', tot_rows, 'rows.'
            exit
          endif
        END DO
      
        close(10)
      
        ! Do stuff, e.g. re-allocate the array
        print *,array(:,:tot_rows)
      
        deallocate(array)
      end program arraycall 
      

      iostat &gt; 0 是错误,iostat &lt; 0 是文件结尾(或某些编译器的记录结尾)。

      【讨论】:

      • 非常感谢亚历山大的回答。这个问题的小扩展。如果文件包含超过 1000 行怎么办?有没有办法让我根本不必关心行数?如果是,那么是否也可以对列进行处理?
      • 您可以只使用一个空白的read 语句来通读文件,计算行数直到最后。 (即用iostat检查)然后就可以分配必要的数组了,用rewind重新开始,把数据放进去。
      • @SuperCow 是的,但这需要两次读取文件。我更喜欢重新分配数组,它只需要在内存中进行操作(而不需要 I/O)。
      • 在我看来,PT2009 要求的是动态分配数组(至少是行大小)。我同意 SuperCow 的观点。您必须通过文件两次(不幸的是)。首先,扫描以获取行数,分配数组,然后再次读取。虽然,如果有像高级语言这样的动态扩展数组。
      • @Indigo 我知道......但特别是对于大文件,重新分配数组甚至扫描文件要快得多。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-04
      • 2014-06-07
      • 2021-07-24
      • 1970-01-01
      • 2019-04-27
      • 1970-01-01
      相关资源
      最近更新 更多