【问题标题】:overwrite a file using fortran使用 fortran 覆盖文件
【发布时间】:2016-11-05 16:58:44
【问题描述】:

我正在使用写入文件的 Fortran 90 程序。该文件的第一行应该指示剩余文件中的行数。当满足某个标准并且无法事先确定时,该文件由程序编写。基本上,只有运行结束后我才会知道总行数。

我想通过以下方式进行:

1) 打开文件并在第一行写入一些文本,例如“Hello

2) 根据需要在文件中写入行,并为行数保留一个计数器

3) 运行结束后,在关闭文件之前,将第一行字符串 ("Hello") 替换为 counter

问题出在第3步,不知道如何替换第一行。

我能想到的另一个选项是写入 2 个文件。首先,像上面那样写一个没有计数器的文件。运行结束后,关闭文件并写入另一个文件,这一次,我知道了计数器的值。

我相信有一种方法可以继续第一种方法。有人可以帮我解决这个问题吗?

【问题讨论】:

    标签: fortran fortran90 overwrite


    【解决方案1】:

    Fortran 支持三种形式的文件访问 - DIRECT、STREAM (F2003+) 和 SEQUENTIAL。 DIRECT 和 STREAM 访问都支持能够重写文件的较早部分,而 SEQUENTIAL 访问则不支持(对较早记录的重写会在重写记录处截断文件)。

    使用直接访问,文件中的所有记录长度相同。任何输入/输出语句都可以[必须]访问任意记录,只需在语句中指定相关记录号即可。但请注意,直接访问文件的典型磁盘格式可能与您对带有“行”的文件的想法不符。

    通过格式化流访问,可以使用 INQUIRE 语句捕获文件中的当前位置,然后稍后的输入/输出语句可以使用 POS 说明符在该位置开始数据传输。格式化流访问文件的典型磁盘格式通常与人们对带行的文本文件的期望相匹配。

    流访问可能是您想要的。两种方法的示例如下所示。

    直接访问:

    PROGRAM direct
      IMPLICIT NONE
    
      INTEGER :: unit
      REAL :: r
      INTEGER :: line
    
      OPEN( NEWUNIT=unit,  &
          FILE='direct.txt',  &
          STATUS='REPLACE',  &
          ACCESS='DIRECT',  &
          RECL=15,  &      ! The fixed record length.
          FORM='FORMATTED' )
    
      CALL RANDOM_SEED()
    
      ! No need to write records in order - we just leave off 
      ! writing the first record until the end.
    
      line = 0
      DO
        CALL RANDOM_NUMBER(r)
        IF (r < 0.05) EXIT
    
        line = line + 1
        PRINT "('Writing line ',I0)", line
        ! All the "data" records are offset by one, to allow the 
        ! first record to record the line count.
        WRITE (unit, "('line ',I10)", REC=line+1) line
      END DO
    
      ! Now update the first record with the number of following "lines".
      WRITE (unit, "(I10)", REC=1) line
    
      CLOSE(unit)
    END PROGRAM direct
    

    流访问:

    PROGRAM stream
      IMPLICIT NONE
    
      INTEGER :: unit
      REAL :: r
      INTEGER :: line
      INTEGER :: pos
    
      OPEN( NEWUNIT=unit,  &
          FILE='stream.txt',  &
          STATUS='REPLACE',  &
          ACCESS='STREAM',  &
          POSITION='REWIND',  &
          FORM='FORMATTED' )
    
      CALL RANDOM_SEED()
    
      ! Remember where we are.  In this case, the position 
      ! is the first file storage unit in the file, but 
      ! it doesn't have to be.
      INQUIRE(unit, POS=pos)
    
      ! Leave some space in the file for later overwriting 
      ! with the number of lines.  We'll stick the number 
      ! zero in there for now.
      WRITE (unit, "(I10)") 0
    
      ! Write out the varying number of lines.
      line = 0
      DO
        CALL RANDOM_NUMBER(r)
        IF (r < 0.05) EXIT
    
        line = line + 1
        PRINT "('Writing line ',I0)", line
        WRITE (unit, "('line ',I10)") line
      END DO
    
      ! Now update the space at the start with the number of following "lines".
      WRITE (unit, "(I10)", POS=pos) line
    
      CLOSE(unit)
    END PROGRAM stream
    

    【讨论】:

    • 感谢您的信息。 “流”的选项是我应该走的路。显然,我的编译器不支持 Access = stream。
    • 我错过了 Fortran 90 标签。 Stream 是 Fortran 2003 的一项功能。
    • 许多现代 Fortran 编译器现在都支持 2003,包括 STREAM - 所以这是一个有价值的答案,即使它在这种特殊情况下不起作用。不过,我可以建议在答案本身中更清楚地说明这仅适用于 2003 兼容的编译器。
    【解决方案2】:

    返回顺序访问文件很棘手,因为行的长度可能不同。如果你改变一行的长度,你必须把所有的东西都移到后面。

    我建议在计算行数时将输出写入暂存文件。然后,一旦完成,倒回暂存文件,将行数写入输出文件,然后将暂存文件的内容复制到该输出文件。

    这就是我所做的:

    program var_file
        implicit none
        character(len=*), parameter :: filename = 'delme.dat'
        integer :: n, io_stat
        character(len=300) :: line
    
        open(unit=200, status='SCRATCH', action="READWRITE")
    
        n = 0
    
        do
            read(*, '(A)') line
            if (len_trim(line) == 0) exit  ! Empty line -> finished
            n = n + 1
            write(200, '(A)') trim(line)
        end do
    
        rewind(200)
    
        open(unit=100, file=filename, status="unknown", action="write")
        write(100, '(I0)') n
    
        do
            read(200, '(A)', iostat=io_stat) line
            if (io_stat /= 0) exit
            write(100, '(A)') trim(line)
        end do
    
        close(200)
        close(100)
    
    end program var_file
    

    【讨论】:

    • 感谢您的详细回复和程序。这是我的备用计划。您节省了我编写代码以重写文件的时间。 :)
    • 我会这样做。我不保证没有更好的我不知道的。
    猜你喜欢
    • 2014-05-08
    • 2013-11-04
    • 2016-11-27
    • 2020-12-29
    • 2019-10-21
    • 2014-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多