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'.
之后
我不希望我的代码在遇到不典型的格式时崩溃。
似乎无法预先确定 'fmt ' 字符串在文件中出现的位置。我的策略是在文件中搜索它,然后简单地丢弃以 'JUNK'.
开头的流氓部分
我最初使用 SCAN
或 INDEX
搜索文件流的尝试失败了,因为传递这些内部函数打开的文件单元号会引发错误,报告文件不是字符串。
到目前为止,阅读我的代码可能会更加清晰。
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 例程在内存中处理它。
我正在编写 Fortran90 代码来读取 .wav 音频文件。
在 .wav 格式中有一个由字符串 'WAVE' 引入的块。在这个块中必须出现两个由字符串 'fmt ' 和 'data'.
引入的子块在我使用的特定 .wav 文件中,为了测试代码,在 'WAVE' 字符串之后,在以 'JUNK' 开头的子块之前,有 36 个字符的间隔=46=] 出现在文件中(下面提供了图片)。
我阅读的在线资源并未表明存在此类差距。期望是 'fmt ' 应该直接出现在 'WAVE'.
之后我不希望我的代码在遇到不典型的格式时崩溃。
似乎无法预先确定 'fmt ' 字符串在文件中出现的位置。我的策略是在文件中搜索它,然后简单地丢弃以 'JUNK'.
开头的流氓部分我最初使用 SCAN
或 INDEX
搜索文件流的尝试失败了,因为传递这些内部函数打开的文件单元号会引发错误,报告文件不是字符串。
到目前为止,阅读我的代码可能会更加清晰。
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 例程在内存中处理它。