gnu FORTRAN 未格式化文件记录标记存储为 64 位宽度?
gnu FORTRAN unformatted file record markers stored as 64-bit width?
我有一个遗留代码和它读取的一些未格式化的数据文件,它适用于 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
个字节。此标记存储在 before 和 after 记录中。但是,我注意到每个记录标记之后的 32 位是 00000000
。因此,要么将记录标记视为 64 位整数(小端),要么在每个记录标记后有一个 32 位零填充。无论哪种方式,新编译器生成的代码都将记录标记读取为 32 位整数,并且不需要任何填充。这实际上是 intrudes/corrupts 正在读入的数据。
有没有简单的方法可以解决这个不可移植的问题?新旧硬件都是 64 位架构,我编译的可执行文件也是。如果我再次尝试使用较旧的编译器版本,它是否会解决问题,或者它是否依赖于硬件?我更喜欢使用较新的编译器,因为它们效率更高,而且我真的不想编辑源代码以将所有文件打开为 access='stream'
并手动读取尾随 0
整数在每个记录标记之后,在每个记录之前和之后。
P.S。如果没有更简单的选择,我可能会编写 C++ 代码来更改数据文件并删除这些零填充。
鉴于 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 手册中的 -frecord-marker= 选项。使用 -frecord-marker=8 您可以读取由旧版本的 gfortran 生成的旧式未格式化顺序文件。
我有一个遗留代码和它读取的一些未格式化的数据文件,它适用于 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
个字节。此标记存储在 before 和 after 记录中。但是,我注意到每个记录标记之后的 32 位是 00000000
。因此,要么将记录标记视为 64 位整数(小端),要么在每个记录标记后有一个 32 位零填充。无论哪种方式,新编译器生成的代码都将记录标记读取为 32 位整数,并且不需要任何填充。这实际上是 intrudes/corrupts 正在读入的数据。
有没有简单的方法可以解决这个不可移植的问题?新旧硬件都是 64 位架构,我编译的可执行文件也是。如果我再次尝试使用较旧的编译器版本,它是否会解决问题,或者它是否依赖于硬件?我更喜欢使用较新的编译器,因为它们效率更高,而且我真的不想编辑源代码以将所有文件打开为 access='stream'
并手动读取尾随 0
整数在每个记录标记之后,在每个记录之前和之后。
P.S。如果没有更简单的选择,我可能会编写 C++ 代码来更改数据文件并删除这些零填充。
鉴于 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 手册中的 -frecord-marker= 选项。使用 -frecord-marker=8 您可以读取由旧版本的 gfortran 生成的旧式未格式化顺序文件。