我目前正在尝试用C ++读取Fortran编写的二进制文件,但我没有取得多大成功。写入文件的Fortran代码不是我自己的,尽管C ++解析例程是。
二进制文件的第一条记录是使用以下语句编写的:
INTEGER var1 var2 var3
WRITE(12,REC=1) var1,var2,var3
执行succcesfull读取的Fortran片段如下所示:
open(unit=10,file="ETC.bin",access='direct',recl=24,iostat=iost,status='old')
read (unit=10,rec=1) var1,var2,var3
close(unit=10)
print*,var1,var2,var3
在C ++方面,我到目前为止已经提出了以下内容:
FILE* binfile = fopen("ETC.bin","rb") ;
fseek (binfile,0,SEEK_END) ;
long lSize = ftell (binfile) ;
char* buffer = (char*) malloc (sizeof(char)*lSize) ;
rewind (binfile) ;
size_t result=fread(buffer,1,96,binfile) ;
for (unsigned i = 0; i<=result; i++){
printf("%f\n",buffer[i]) ;
}
不幸的是,我的C ++ printf语句返回废话。请注意,我假设Fortran依赖于4位字(例如gfortran编译器),并且如果使用ifort,那么
--assume byterecl
在编译时需要选项。
我知道结果应该是什么,但我不确定如何在C ++中复制Fortran读取语句的行为。
感谢您的帮助!
P.S。此处发布了类似的问题:reading fortran binary file in c++,指向以下dead link。没有太多信息,或者我的Google-Fu很糟糕。
答案 0 :(得分:2)
我在C方面不是很好,但我尝试了一些事情。
首先是Fortran部分:
program direct_access
implicit none
integer, parameter :: UNT = 63347
open(unit=UNT, file='delme.unf', access='DIRECT', &
form='UNFORMATTED', status='REPLACE', recl=24)
write(UNT, rec=1) 1, 2, 3
write(UNT, rec=2) 4, 5, 6
close(UNT)
end program direct_access
我正在将3个整数(每个4个字节)写入一个记录长度为24个字节的无格式文件中。 (注意:我假设记录长度以字节为单位,显然不能保证,编译器和系统依赖。)
此外,来自my preferred Fortran book
未格式化的直接addess文件比格式化的直接访问文件更小,更快,但它们不能在不同类型的处理器之间移植。
(除非在打开文件时特别指定FORM='FORMATTED'
,否则它将被取消格式化。)
测试数据是否写得正确:
$ hexdump delme.unf
0000000 0001 0000 0002 0000 0003 0000 0000 0000
0000010 0000 0000 0000 0000 0004 0000 0005 0000
0000020 0006 0000 0000 0000 0000 0000 0000 0000
0000030
看起来不错。请注意,记录长度(24字节)大于数据(3 * 4字节),因此内部有未使用的数据块。
现在进入C计划,而不是我的专业知识:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
off_t fsize(const char *filename) {
struct stat st;
if (stat(filename, &st) == 0)
return st.st_size;
return -1;
}
int main(){
int record_size=24;
int num_records=fsize("delme.unf") / record_size;
FILE* binfile = fopen("delme.unf","rb") ;
int* record = (int*) malloc (record_size) ;
size_t result ;
for (unsigned j=0; j < num_records; j++) {
fseek(binfile, j * record_size, SEEK_SET) ;
printf("%i : ", j) ;
result=fread(record,sizeof(int),record_size/sizeof(int),binfile) ;
for (unsigned i = 0; i<result; i++){
printf("%i ",record[i]) ;
}
printf("\n");
}
free(record);
fclose(binfile);
}
输出:
0 : 1 2 3 0 0 0
1 : 4 5 6 0 0 0
也很好。
我注意到的一些事情:
char
- 表示每个元素有一个字节。但是整数有4个字节。这意味着文件内容被分成几个元素。result
个元素,那么buffer
的索引需要从0
转到result-1
。%f
作为输出,表示浮动?但我认为这些都是彗星?当然,如果你不关心无序读取数据,你可以循环遍历文件:
#include <stdlib.h>
#include <stdio.h>
int main() {
FILE* data = fopen("delme.unf", "rb") ;
int var ;
while (! feof(data )) {
fread(&var, sizeof(int), 1, data);
printf("%i ", var);
}
printf("\n");
fclose(data);
}
当然有些人会帮助你编写比我更好的C代码。
答案 1 :(得分:0)
编辑请将此作为新答案
你的问题在这部分
for (unsigned i = 0; i<=result; i++){
printf("%f\n",buffer[i]) ;
}
首先,i<=result
应为i<result
。否则它从buffer
读取(结果+ 1)个字节。这是C / C ++初学者的常见错误。在C / C ++中,如果遍历具有N个元素的数组,则将索引从0遍历到N-1。
其次,i++
应为i+=4
。 Fortran INTEGER
类型和C int
类型通常为4个字节。
最后,printf("%f\n",buffer[i])
应为printf("%d\n",(int)buffer[i])
。 %f
中的printf
采用浮点数。要打印整数,请使用%d
。 (int)buffer[i]
使程序将缓冲区[i]重新解释为原始int
类型的char
类型。没有它可能不会导致错误,但编译器通常会抱怨。
EDIT2 也许您必须使用*((int*)(&buffer[i]))
代替(int)buffer[i]
。
替代方法是使用4个字节的整数数组。在这种情况下,代码变得如下。 uint32_t
是4字节整数类型。通常int
类型是4个字节,但C标准表示它可能是2个字节。所以使用uint32_t
是安全的。
FILE* binfile = fopen("ETC.bin","rb") ;
fseek (binfile,0,SEEK_END) ;
long lSize = ftell (binfile) ;
rewind (binfile) ;
if(lSize >= 24*sizeof(uint32_t)){
uint32_t array[24];
fread(array,sizeof(uint32_t),24,binfile) ;
for (int i = 0; i<24; i++){
printf("%d\n",array[i]) ;
}
}else{
printf("file size is too small.\n");
}
编辑以下答案与您的问题无关。
Fortran在无格式模式下的写入例程会自动将页眉/页脚添加到主数据中。页眉/页脚是包含数据大小的二进制字符串。页眉/页脚的长度由编译器确定。
这是一些fortran程序的二进制输出。
$xdd fort.20
00000000: 4000 0000 6664 6532 6265 616d 2020 2020 @...fde2beam
00000010: 2020 2020 2020 2020 0432 0c00 7e03 0000 .2..~...
00000020: 7e03 0000 f059 34e7 a9d5 853f 8534 5ad5 ~....Y4....?.4Z.
00000030: 0910 1340 f059 34e7 a9d5 853f 8534 5ad5 ...@.Y4....?.4Z.
00000040: 0910 1340 4000 0000
在上面的示例中,40000000是页眉/页脚。由于40000000是64,因此页眉和页脚之间必须有64个字节。现在,在显示的十六进制文本中,您可以看到页脚和标题之间有32 * 4个十六进制。因为2hex = 1byte,实际上页脚/标题之间有64个字节。
因此,当您使C程序读取fortran无格式二进制文件时,您的程序需要明智地跳过或使用这些页眉/页脚。