使用 fortran 覆盖文件

问题描述 投票:0回答:3

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

我想按以下方式进行:

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

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

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

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

我能想到的另一个选择是写入 2 个文件。首先,如上所示编写一个不带计数器的文件。运行结束后,关闭文件并写入另一个文件,这一次,我知道计数器的值。

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

fortran fortran90 overwrite
3个回答
4
投票

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

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

0
投票

对 IanH 建议使用 Stream 作为解决方案的评论: Intel 的 Fortran 版本 2021.8 手册注释:

“在未发生错误的格式化流 WRITE 后,输出文件在具有最大 POS 值的字节后被截断。未格式化的流 WRITE 不会截断输出文件。”

流思想的变化都会导致文件在倒回和位置写入后被截断。

英特尔® Fortran 编译器经典版和英特尔® Fortran 编译器开发人员指南和参考第 55 页: “完全支持 Fortran 2018 的 Fortran 编译器。”

© www.soinside.com 2019 - 2024. All rights reserved.