将Fortran二进制输出转换为Visual Basic .NET

问题描述

我们有一个基于Intel编译器的小型Fortran程序,该程序可输出二进制数据。我正在尝试将该代码重构为.NET。我研究了Fortran中的二进制编写方式是如何工作的,但是有点迷茫。使用相同的输入,我们不会获得相同的二进制输出。我想念什么?

Fortran程序基本上如下所示:

CHaraCTER * 3  dept(400)
CHaraCTER * 4  code(30)
CHaraCTER * 7  zdate(400)
REAL           wt(400),WT99(400),WT90(400),WT50(400) 
Integer        ops(400),J,K

OPEN( 7,FILE = 'C\stats.BIN',ACCESS='SEQUENTIAL',FORM='UNFORMATTED')

for each record
    Write(7) code(I),( dept(K),wt(K),ops(K),zdate(K),WT99(K),WT90(K),WT50(K),K = 1,J )
next record

我尝试重写.NET

Public Class CityStats
    Public Property Dept As String
    Public Property Weight As Integer
    Public Property NumOps As Integer
    Public Property LastOpDate As DateTime
    Public Property CheckDate As DateTime
    Public Property 99percentile As Double
    Public Property 90percentile As Double
    Public Property 50percentile As Double
End Class


Dim city As String = "BB",xcode as Integer = 1

Dim str As Stream = File.Open"C:\stats.BIN",FileMode.Create)
Using bw As BinaryWriter = New BinaryWriter(str)
      Dim sb As New StringBuilder(4,4)
      sb.Append(city & xcode.ToString("00"))
      bw.Write(sb.ToString)
      bw.Write(cityStats.Count)

      'cycle through all the records
      For Each out In CityStats
          bw.Write(out.Dept)
          bw.Write(out.Weight)
          bw.Write(out.NumOps)
          bw.Write(out.CheckDate.ToString("ddMMMyy").toupper)
          bw.Write(out.99percentile)
          bw.Write(out.90percentile)
          bw.Write(out.50percentile)
      Next out
 End Using

解决方法

好吧,我被束缚了,但最终找到了解决我问题的方法。最后,关键是理解数据类型以及它们在FORTRAN和.NET之间的映射。如果我们以上述示例为例,那么我将更新代码以复制由FORTRAN程序生成的二进制文件。

我通过获取两个二进制文件并通过F90二进制读取器运行它们来进行验证。所有数据均具有可比性。就是说,字符串和整数是精确的,而单打则相当接近。对于此过程,单个数据在十分之一以内,对我来说已经足够了,因为一些数据是四舍五入的。

Public Class CityStats
    Public Property Dept As String
    Public Property Weight As Integer
    Public Property NumOps As Integer
    Public Property LastOpDate As DateTime
    Public Property CheckDate As DateTime
    Public Property 99Percentile As Double
    Public Property 90Percentile As Double
    Public Property 50Percentile As Double
End Class


Dim city As String = "BB",xcode as Integer = 1

'open a main BinaryWriter
Dim str As Stream = File.Open"C:\stats.BIN",FileMode.Create)
Using bw As BinaryWriter = New BinaryWriter(str)
      'write to a temp BinaryWriter so we can determine the record length before we write it to the main BinaryWriter.
      Dim msRec As New MemoryStream
      Dim bwRec As New BinaryWriter(msRec)

      Dim cx As String = F90FixedString(city & xcode.ToString("00"),3)
      bwRec.Write(cx.ToCharArray)   'see note below about strings vs char arrays
      bwRec.Write(cityStats.Count)

      'cycle through all the records for one X code and write out to binary. in this case a FORTRAN REAL defaults
      'to a 4 byte single precision float or in .NET it is a type Single. Also we do not write out a string as the
      'BinaryWriter will prepend it with the string length. instead we convert the string to an array of characters.
      'The Fortran Integer is a 4-byte signed integer by default so that is equivalent to the .NET Integer type.
      For Each out In CityStats
          bwRec.Write(out.Dept.ToCharArray)
          bwRec.Write(out.Weight)
          bwRec.Write(out.NumOps)
          bwRec.Write(out.CheckDate.ToString("ddMMMyy").ToUpper.ToCharArray)
          bwRec.Write(CSng(out.99Percentile))
          bwRec.Write(CSng(out.90Percentile))
          bwRec.Write(CSng(out.50Percentile))
      Next out

      'get record length in bytes. Note: BaseStream.Length is 64-bit but my data is rather small so am confident I will never get to > 32-bit
      Dim recLenInBytes As Integer = CInt(bwRec.BaseStream.Length)

      'now write the temp BinaryWriter to our main BinaryWriter. preceded and followed by a 32-bit integer
      'containing the record length.
      bw.Write(recLenInBytes)
      bw.Write(msRec.ToArray)
      bw.Write(recLenInBytes)
 End Using


'a fixed length string in FORTRAN is left-justified and by default conisist of spaces.
Public Function F90FixedString(ByVal inp As String,ByVal strLen As Integer) As String
    'checks
    If inp Is Nothing Then Throw New Exception("Null string. Can't create a fixed length string.")

    'if the string is already the required length then just return the input string
    If inp.Length = strLen Then Return inp

    'pad the string to the right with spaces if required
    Dim out As String = inp.PadRight(strLen," "c)

    'now check to see if the new string is longer than required and if so then we take the
    'proper amount of characters starting from the left.
    If out.Length > strLen Then out = out.Substring(0,strLen)

    'return the new string
    Return out
End Function

信息很难找到,其中有些已经过时了。有了这些链接和一些反复试验,一切都解决了。我使用的是Lahey F90编译器,因此根据您使用的特定编译器和设置,您的结果可能会有所不同。

Opening Binary Files in Fortran: Status,Form,Access https://software.intel.com/content/www/us/en/develop/documentation/fortran-compiler-developer-guide-and-reference/top/compiler-reference/data-and-i-o/fortran-i-o/record-length.html

未格式化的文件(FORM ='UNFORMATTED'):以4字节为单位指定记录长度,除非您指定了bytebytel编译器假定为1字节为单位。

https://community.intel.com/t5/Intel-Fortran-Compiler/Convert-REAL-8-Unformatted-Sequential-file/td-p/771973

Visual Fortran SEQUENTIAL UNFORMATTED文件格式在UNIX系统(包括DIGITAL UNIX)上非常常见。每个Fortran“记录”的前面和后面 由包含记录长度的32位整数组成(该整数存储在“ little-endian”布局中,最低有效位位于最低寻址字节中。)此记录长度是应用Fortran记录语义所必需的,包括使用BACKSPACE的能力。不幸的是,这在《程序员指南》中没有正确记录-所描述的布局实际上是Microsoft Fortran PowerStation的布局。这将在以后的版本中得到纠正。

Why does BinaryWriter prepend gibberish to the start of a stream? How do you avoid it? https://software.intel.com/content/www/us/en/develop/documentation/fortran-compiler-developer-guide-and-reference/top/compiler-reference/data-and-i-o/fortran-i-o/record-length.html

.NET BinaryWriter.Write() Method -- Writing Multiple Datatypes Simultaneously https://docs.microsoft.com/en-us/dotnet/api/system.io.binarywriter.write?view=netframework-4.7

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...