【问题标题】:gnu FORTRAN unformatted file record markers stored as 64-bit width?gnu FORTRAN 未格式化的文件记录标记存储为 64 位宽度?
【发布时间】:2015-08-18 05:27:12
【问题描述】:

我有一个遗留代码和它读取的一些未格式化的数据文件,它可以与 gnu-4.1.2 一起使用。我无权访问最初生成这些数据文件的方法。当我使用较新的 gnu 编译器 (gnu-4.7.2) 编译此代码并尝试在另一台计算机上加载旧数据文件时,读取它们时遇到困难。我首先打开文件并读取由三个 32 位整数组成的第一条记录:

open(unit, file='data.bin', form='unformatted', status='old')
read(unit) x,y,z

我希望这三个整数在这里描述 x,y,z 跨度,以便接下来它可以加载具有相同维度的 float 值的 3D 矩阵。但是,它会为第一个值加载 0,然后将接下来的两个值偏移。

期待:

x=26, y=127, z=97    (1A, 7F, 61 in hex)

已加载:

x=0, y=26, z=127     (0, 1A, 7F in hex)

当我在十六进制编辑器中检查数据文件时,我想我知道发生了什么。

在这种情况下,第一个记录标记的值为12(十六进制的0C),因为它读取三个整数,每个整数为4 字节。此标记存储在记录之前之后。但是,我注意到每个记录标记之后的 32 位是00000000。因此,要么记录标记被视为 64 位整数(小端序),要么在每个记录标记之后有一个 32 位零填充。无论哪种方式,使用新编译器生成的代码都将记录标记读取为 32 位整数,并且不需要任何填充。这有效地侵入/破坏了正在读取的数据。

有没有简单的方法来解决这个不可移植的问题?新旧硬件都是 64 位架构,我编译的可执行文件也是如此。如果我再次尝试使用较旧的编译器版本,它会解决问题,还是取决于硬件?我更喜欢使用较新的编译器,因为它们效率更高,而且我真的不想编辑源代码以将所有文件打开为 access='stream' 并在每个记录标记之后手动读取尾随 0 整数,在每条记录之前和之后。

附:如果没有更简单的替代方法,我可能会编写一个 C++ 代码来更改数据文件并删除这些零填充。

【问题讨论】:

  • 有没有简单的方法来解决这个不可移植的问题?没有。这是大多数二进制文件读写的一个特性,当然对于 Fortran,表单和布局二进制文件的数量从未被标准化。当你遇到同样的问题时,你似乎在做我们大多数人所做的事情,打开一个十六进制编辑器,做一些探索性的阅读,挠头,考虑将文件修改为另一种二进制格式,这将与下一个程序不兼容读取文件,...

标签: file fortran gnu


【解决方案1】:

请参阅 gfortran 手册中的 -frecord-marker= 选项。使用 -frecord-marker=8,您可以读取由旧版本 gfortran 生成的旧样式无格式顺序文件。

【讨论】:

  • 这正是我所需要的。 "为无格式文件指定记录标记的长度。长度的有效值为 4 和 8。默认值为 4。这与以前版本的 gfortran 不同,后者在大多数系统上指定默认记录标记长度为 8。如果你想读取或写入与早期版本的 gfortran 兼容的文件,请使用 -frecord-marker=8。"
【解决方案2】:

鉴于 Fortran 对此没有标准化,我选择将数据文件转换为使用 32 位宽记录长度而不是 64 位宽的新格式。以防将来有人需要这样做,我在这里包含了一些对我有用的 Visual C++ 代码,并且应该可以轻松地修改为 C 或其他语言。我还上传了Windows executable (fortrec.zip) here

CFile OldFortFile, OutFile; 
const int BUFLEN = 1024*20;
char pbuf[BUFLEN];
int i, iIn, iRecLen, iRecLen2, iLen, iRead, iError = 0;

CString strInDir = "C:\folder\";
CString strIn = "file.dat";
CString strOutDir = "C:\folder\fortnew\"
system("mkdir \"" + strOutDir + "\""); //create a subdir to hold the output files
strIn = strInDir + strIn;
strOut = strOutDir + strIn;
if(OldFortFile.Open(strIn,CFile::modeRead|CFile::typeBinary)) {
    if(OutFile.Open(strOut,CFile::modeCreate|CFile::modeWrite|CFile::typeBinary)) {
        while(true) {
            iRead = OldFortFile.Read(&iRecLen, sizeof(iRecLen)); //Read the record's raw data
            if (iRead < sizeof(iRecLen)) //end of file reached
                break;
            OutFile.Write(&iRecLen, sizeof(iRecLen));//Write the record's raw data
            OldFortFile.Read(&iIn, sizeof(iIn));
            if (iIn != 0) {//this is the padding we need to ignore, ensure it's always zero
                //Padding not found
                iError++;
                break;
            }
            i = iRecLen;
            while (i > 0) {
                iLen = (i > BUFLEN) ? BUFLEN : i;
                OldFortFile.Read(&pbuf[0], iLen);
                OutFile.Write(&pbuf[0], iLen);
                i -= iLen;
            }
            if (i != 0) { //Buffer length mismatch  
                iError++;
                break;
            }
            OldFortFile.Read(&iRecLen2, sizeof(iRecLen2));
            if (iRecLen != iRecLen2) {//ensure we have reached the end of the record proeprly
                //Record length mismatch
                iError++;
                break;
            }
            OutFile.Write(&iRecLen2, sizeof(iRecLen));
            OldFortFile.Read(&iIn, sizeof(iIn));
            if (iIn != 0) {//this is the padding we need to ignore, ensure it's always zero
                    //Padding not found
                    break;
            }
        }
        OutFile.Close();
        OldFortFile.Close();
    }
    else { //Could not create the ouput file.
        OldFortFile.Close();
        return;
    }
}
else { //Could not open the input file

}
if (iError == 0)
    //File successfully converted
else
    //Encountered error

【讨论】:

  • 请注意,此代码不支持 gfortran 在使用 32 位记录标记时支持大于 2 GB 的记录的子记录方案。也许对你或大多数人来说不是问题,但仍然......
  • 原来这可以用一个简单的编译器选项“-frecord-marker=8”来处理
猜你喜欢
  • 2013-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-14
  • 1970-01-01
  • 2021-08-16
  • 2019-01-16
  • 1970-01-01
相关资源
最近更新 更多