在C ++中读取直接访问fortran无格式文件

时间:2015-05-06 00:31:52

标签: c++ fortran

我目前正在尝试用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很糟糕。

2 个答案:

答案 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个字节。这意味着文件内容被分成几个元素。
  • 另外,你的fortran代码设置的记录长度为24(字节,我假设),但是3个整数每个只使用4个字节,因此不使用记录的一半。这就是为什么read给出了三个零。
  • 如果您有result个元素,那么buffer的索引需要从0转到result-1
  • 确定文件大小的方式显然不是一个好主意,请参阅here
  • 您使用%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无格式二进制文件时,您的程序需要明智地跳过或使用这些页眉/页脚。