使用 fortran 覆盖文件

overwrite a file using fortran

我正在使用写入文件的 Fortran 90 程序。该文件的第一行应该指示剩余文件中的行数。该文件是在满足特定条件且无法事先确定时由程序写入的。基本上,等到运行结束了,我才会知道总行数。

我想按以下方式进行:

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

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

3) 一旦 运行 结束并在关闭文件之前,将第一行字符串 ("Hello") 替换为 计数器.

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

我能想到的另一个选择是写入 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

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

通过直接访问,文件中的所有记录长度相同。通过简单地在语句中指定相关记录号,任何 input/output 语句都可以 [必须] 访问任意记录。但请注意,直接访问文件的典型磁盘格式可能与您对 "lines".

文件的想法不符

通过格式化流访问,可以使用 INQUIRE 语句捕获文件中的当前位置,然后稍后的 input/output 语句可以使用 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