首页 > 教程 > 正文

阅读排行

Fortran流文件-读写二进制任意位置
2014-11-07 17:02:02   来源:Fcode研讨团队   评论:0 点击:

本文以读写Surfer网格化 grd 文件为例,介绍了Fortran新增的流文件(stream)读取方式,较之直接读取法更方便更灵活。对结构不规则的二进制文件有很好的读写效果。

一. Fortran 读取二进制文件的直接读取法

本站曾发布文章:《Fortran 二进制文件读写》,介绍了以直接读取法进行二进制文件读写的方法。阅读本文前,如有必要,您可先阅读此文,以便了解二进制文件的基本概念。

直接读取(Access = 'Direct' )时,需指定记录长度(RecL)。

而不少二进制文件,其结构不规则,间或有 2 字节、4 字节 或 8 字节、甚至其他长度的数据,例如 SEGY 记录、Surfer 的 grid 网格化文件,此时使用直接读取会显得非常笨拙,常常需要多次 Open 同一个文件,使用不同的 RecL,以便读写特定的数据。

网络上曾有不少文章,介绍可以自由读写任意字节或位置的 fortran 代码,但通常使用扩展的语法实现。或者通过调用操作系统的 API 实现,不规范、只适合一部分编译器。

二. 流文件的基本用法

流文件(access="stream")是 Fortran2003 新增的一种读写方式,目前主流的尚在更新的编译器均支持。(包括但不限于 IVF、GFortran、PGI、NAG、Lahey、Ftn95、Absoft ,不含 CVF 和 PowerStation )

它是一种读写方式,而不是文件本身的格式。它并不把二进制文件视为一个一个的“记录”,而是视为一个整体。因此,无需指定记录长度(RecL)。

文件打开后,操作位置就在文件的第一个字节上,每次读取多少字节,就自动向后移动多少字节。下一次读写紧接着此处。

同时,流文件也提供任意位置(字节数),以及查询当前位置的方法。

1. 打开流文件
一个典型的流文件读写二进制,可使用以下语句:
Open( 12 , File = "fcode_test.bin" , access="stream" , form = "unformatted" )
access 指定打开方式为流文件,form 指定文件为无格式文件(二进制文件)

2.读写
读写时也无需指定记录(rec),典型的读取语句:
Read( 12 ) 变量1 , 变量2 , 变量3...... 变量 N
此时,在当前操作位置读写。读写后,操作位置自动向后移动 M 个字节(M等于所有读写的变量占有的字节数)
注意:无格式读写,不能指定格式,也不能写 * 

如果想在其他位置读写,可使用这样的语句:
Read( 12 , pos = i ) 变量1 , 变量2 , 变量3...... 变量 N
此时,在第 i 个字节处开始读取。读写后,操作位置相对 i 向后移动 M 个字节(M 同上)

3.查询当前操作位置
经过若干 read 语句或 write 语句后,可能就不知道当前读取位置在哪儿了。此时,可通过 Inquire 查询:
Inquire( 12 , Pos = i )
以上语句执行后,i 的值会变为 12 号文件当前的操作位置(字节数)

4.设置当前操作位置
我们经常要跳过若干字节,或回退若干字节进行读写。这可以通过查询操作位置后,加减一定数量,再设置为当前操作位置。例如:
Inquire( 12 , Pos = i )
Read( 12 , Pos= i + 32 , iostat = iErr )
用 Read 语句来设置当前位置为 i + 32(即向后跳过 32 字节),而 Read 后面没有任何变量。
iostat = iErr 可以防止超过文件大小而出错。

5.关闭流文件
使用 Close(12)  关闭,与常规文件没有区别。

可以看出,流文件方式读写,比直接读写更简单,更容易理解。


三. 读取 Surfer 网格化 grd 文件的范例

Surfer 是 Goldensoft 公司出品的一款绘图软件,以等值线绘制为特长,广泛应用在地质、水文等领域。其 grd 文件为(二维)网格化文件。

这种文件有两个版本。6.0 的单精度版本、7.0以上的双精度版本。在这里,我们读取 6.0 的单精度版本,因为它比 7.0 更不规则,更能体现流文件的强大与方便。

以下是它的格式说明
\

可以看出,里面有 4 字节的 char(标识),2 字节的 short(网格大小),8 字节的 double(网格边界:XYZ的最小值最大值),以及 4 字节的 float(网格矩阵)

如果使用直接读写,需要分别考虑读写不同长度数据的记录长度 RecL,多次打开文件。而使用流文件方式,就非常的方便了。
Program www_fcode_cn
  Use, Intrinsic :: ISO_C_BINDING !// 使用内部模块,保持与 C 语言变量类型一致
  Implicit None
  character(len=4) :: flag !// 4字节的标识,必须为 “DSBB”
  Integer(kind=C_SHORT) :: m , n !// 网格大小
  Real(Kind=C_DOUBLE) :: rXMin , rXMax !// X Y Z 的边界
  Real(Kind=C_DOUBLE) :: rYMin , rYMax
  Real(Kind=C_DOUBLE) :: rZMin , rZMax
  Real , allocatable :: d(:,:) !// 网格矩阵
  integer :: i
  Open( 12 , File = "fcode_test.grd" , access="stream" , form = "unformatted" )
  Read( 12 ) flag
  Write( * , * ) 'flag=' , flag
  Read( 12 ) m , n
  Write( * , * ) 'M/N=' , m , n
  allocate( d( m , n ) )
  Read( 12 ) rXMin , rXMax , rYMin , rYMax , rZMin , rZMax
  write( * , * ) 'X_Min=' , rXMin , 'X_Max=' , rXMax
  write( * , * ) 'Y_Min=' , rYMin , 'Y_Max=' , rYMax
  write( * , * ) 'Z_Min=' , rZMin , 'Z_Max=' , rZMax
  Inquire( 12 , Pos = i ) !// 查询当前位置
  write( * , '(a,1x,i4,1x,a)' ) '头部信息一共',i-1,'字节' !//当前操作位置 i 字节
  Read( 12 ) d(:,:)
  Do i = 1 , n
    write( * , '(999f6.3)' ) d(:,i)
  End Do
  Close( 12 )
End Program www_fcode_cn

以上代码有注释,相信很容易理解。您可以点这里下载示范数据:focde_test.grd

如果您对派生类型(type)有一定了解,您还可以更方便的一次读取整个头部信息。
Program www_fcode_cn
  Use, Intrinsic :: ISO_C_BINDING !// 使用内部模块,保持与 C 语言变量类型一致
  Implicit None
  Type , Bind(C) :: GRAD_HEAD
    character(len=4) :: flag !// 4字节的标识,必须为 “DSBB”
    Integer(kind=C_SHORT) :: m , n !// 网格大小
    Real(Kind=C_DOUBLE) :: rXMin , rXMax !// X Y Z 的边界
    Real(Kind=C_DOUBLE) :: rYMin , rYMax
    Real(Kind=C_DOUBLE) :: rZMin , rZMax
  End Type GRAD_HEAD
  Type( GRAD_HEAD ) :: Head
  Real , allocatable :: d(:,:) !// 网格矩阵
  integer :: i  
  Open( 12 , File = "fcode_test.grd" , access="stream" , form = "unformatted" )
  Read( 12 ) HEAD
  allocate( d( HEAD%m , HEAD%n ) )
  Write( * , * ) 'flag=' , HEAD%flag
  Write( * , * ) 'M/N=' , HEAD%m , HEAD%n
  write( * , * ) 'X_Min=' , HEAD%rXMin , 'X_Max=' , HEAD%rXMax
  write( * , * ) 'Y_Min=' , HEAD%rYMin , 'Y_Max=' , HEAD%rYMax
  write( * , * ) 'Z_Min=' , HEAD%rZMin , 'Z_Max=' , HEAD%rZMax
  Inquire( 12 , Pos = i ) !// 查询当前位置
  write( * , '(a,1x,i4,1x,a)' ) '头部信息一共',i-1,'字节' !//当前操作位置 i 字节
  Read( 12 ) d(:,:)
  Do i = 1 , HEAD%n
    write( * , '(*(f6.3))' ) d(:,i)
  End Do
  Close( 12 )
End Program www_fcode_cn
输出结果应为:
\

相关热词搜索:流文件 stream 二进制文件

上一篇:Fortran的位运算
下一篇:FAQ 之 common,Module共享数据的优缺点

分享到: 收藏