Fortran 如何在未格式化的二进制文件中找到字符串?

How can Fortran find a string in an unformatted binary file?

我正在编写 Fortran90 代码来读取 .wav 音频文件。

在 .wav 格式中有一个由字符串 'WAVE' 引入的块。在这个块中必须出现两个由字符串 'fmt ' 和 'data'.

引入的子块

在我使用的特定 .wav 文件中,为了测试代码,在 'WAVE' 字符串之后,在以 'JUNK' 开头的子块之前,有 36 个字符的间隔=46=] 出现在文件中(下面提供了图片)。

我阅读的在线资源并未表明存在此类差距。期望是 'fmt ' 应该直接出现在 'WAVE'.

之后

.wav file format description

我不希望我的代码在遇到不典型的格式时崩溃。

似乎无法预先确定 'fmt ' 字符串在文件中出现的位置。我的策略是在文件中搜索它,然后简单地丢弃以 'JUNK'.

开头的流氓部分

我最初使用 SCANINDEX 搜索文件流的尝试失败了,因为传递这些内部函数打开的文件单元号会引发错误,报告文件不是字符串。

到目前为止,阅读我的代码可能会更加清晰。

program main
  
  use iso_fortran_env

  !=========================================================================

  !Variables for .wav header.
  character(4)     :: ChunkID = '____'
  integer  (4)     :: FileSize
  character(4)     :: Wave = 'WAVE'

  !fmt need only be charcter(4) but is extended here for illustation output.
  character(40)    :: fmt = 'fmt '
  
  !=========================================================================

  !Working variables for file handling..
  integer  (1)  :: args
  character(30) :: file
  integer :: stat

  !Exit when no file name is supplied. 
  args = command_argument_count()
  if(args.ne.1)then
     print *
     print *, 'Error. Enter .wav file name'
     print *, 'Example: cat'
     print *, "NB. The '.wav' extension is assumed. You don't need to add it."
     stop
  end if
  call GET_COMMAND_ARGUMENT(1,file)

  !Construct .wav file name.
  file =  trim(file) // '.wav'

  !Try opening .wav file with name supplied
  OPEN(UNIT=1, iostat=stat, FILE=file, &
       form='unformatted', access='stream', status='old')

  !Test file status and exit on error.
  if(stat.ne.0) then
     write(*,'(a)') 'No known file named ', file
     stop
  end if
  print *, 'File existence test: Passed'

  ! Header read.
  read(1) ChunkID, FileSize, Wave, fmt
  print *, 'ChunkID: ', ChunkID
  print *, 'FileSize: ', FileSize
  print *, '"WAVE": ', wave
  print *, '"fmt ":', fmt

END PROGRAM MAIN

程序使用我下载的试用 .wav 文件生成的输出是这样的:

问题始于结尾 fmt 之前 "fmt ": 之后的不需要的文本。

我的目的是丢弃这个多余的字符串,然后继续从预期的字符串中读取文件 'fmt '。

我应该在 Fortran 中使用什么内在函数来吸收和丢弃无用的文件内容,在包含各种数据类型的文件中保留我需要的部分?

我用这个子程序来改变搜索字符串后面的文件位置str:

subroutine skip_to(str, stat)
  character(*), intent(in) :: str
  integer, intent(out) :: stat
  character :: ch
  integer :: io

  do
    read(unit, iostat=io) ch

    if (io/=0) then
      stat = 1
      return
    end if

    if (ch==str(1:1)) then
      call check(str(2:), stat)
      if (stat == 0) return
    end if

  end do
end subroutine

subroutine check(str, stat)
  character(*), intent(in) :: str
  integer, intent(out) :: stat
  character :: ch
  integer :: i, io

  stat = 1
  i = 0

  do
    i = i + 1

    read(unit, iostat=io) ch

    if (io/=0) return

    if (ch/=str(i:i)) return

    if (i==len(str)) then
      stat = 0
      return
    end if
  end do
end subroutine

它可能非常低效,因为它一次读取一个字节以最大限度地简化操作。它只是读取一个字节并检查字符串是否可能从那里开始,然后检查下一个字节是否正确等等。


请注意,我经常需要在非常大的 vtk 文件(千兆字节)中搜索字符串。

如果你真的只有一小块header。我会将整个 header 读入一个长字符串并使用 string-oriented 例程在内存中处理它。