代码之家  ›  专栏  ›  技术社区  ›  MasterHD

gnu FORTRAN未格式化文件记录标记存储为64位宽?

  •  3
  • MasterHD  · 技术社区  · 9 年前

    我有一个遗留代码和一些它读取的未格式化数据文件,它与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 具有相同尺寸的值。然而,它正在加载 0 对于第一个值,然后对下两个值进行偏移。

    期望:

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

    加载:

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

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

    hex editor

    在这种情况下,第一个记录标记的值为 12 ( 0C 十六进制),因为它在 4 每个字节。此标记存储在 之前 之后 记录。然而,我注意到每个记录标记后面的32位是 00000000 因此,要么将记录标记视为64位整数(小Endian),要么在每个记录标记之后有32位零填充。不管怎样,用新编译器生成的代码都将记录标记读取为32位整数,而不需要任何填充。这会有效地侵入/破坏正在读入的数据。

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

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

    2 回复  |  直到 9 年前
        1
  •  2
  •   janneb    9 年前

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

        2
  •  0
  •   MasterHD    9 年前

    鉴于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