Fortran Coder

标题: 关于二进制文件的读入问题? [打印本页]

作者: evanustc    时间: 2020-5-15 22:14
标题: 关于二进制文件的读入问题?
      纯新人一枚,由于科研需要,刚学77不久,在UBUNTU下解析一个由F77写的远古代码,我用CODEBLOCK移植到了WIN平台下,在跟踪代码过程中,一个子程序中读取了一个二进制文件,关于这里READ的用法,我没太搞清楚,希望得到指点。      读入二进制文件是这么写的,如图1所示。关键部分:
   OPEN(UNIT=IOPDS,FILE=FILNAM(1:LNFILE),ERR=100, STATUS='UNKNOWN',
     &     ACCESS='SEQUENTIAL', FORM='UNFORMATTED',IOSTAT=IOS)
                  ENDIF
       READ(UNIT=IOPDS,ERR=300,IOSTAT=IOS)  LNOW,(WORK(I),I=1,LNOW)    这个read后面的LNOW是什么类型,因为前面也没有显示声明。。?因为WORK在前面已经申明是WORK(1),那么这里是从二进制文件FILE(INQUIRE存在)中读入全部数据后放到LNOW中,在赋值到WORK(1)中吗? 最重要的是我设断点跟踪的过程中,发现执行完READ后,LNOW等于0(执行前为2000多),WORK为0,参见图2,这是说明二进制文件中的所有数据没有读入进去吗??还是我理解有问题,如果读进去了,为啥我用IDE的WATCH栏看到的是0。
      看了论坛上的帖子http://fcode.cn/guide-4-1.html帮助很大,里面是分行读入,我碰到的情况似乎和帖子所讲有出入,这个程序是用一个LNOW把二进制的所有数据都读入了吗?
       这个地方的理解,对我后面阅读学习这个程序非常关键,今天整了一天,还是没整明白,明明文件存在,也OPEN了,读进去为啥LNOW是0,不知道我哪里没理解对这个程序的意思. 另外这些代码在UBUNTU下编译跑例子都正常,也就是说这块代码是没问题的。只是放在WIN下我想用CODEBLOCK能够快速阅读,方便快速理解,应该也不会是平台的差异导致的。谢谢大家的帮助和指点。


       另外我单独把这个读取部分拿出来用77模仿了下,其中FILENAME具体化,执行前后LNOW和WORK皆为0.
       PROGRAM READTXT
       LOGICAL EX
       DIMENSION WORK(1)
      INQUIRE(FILE='FASTLIB',EXIST=EX)
      IF(.NOT.EX) THEN
                  WRITE (*,*) "Hello World!"
                  RETURN
                  ELSE   
     OPEN(UNIT=66,FILE='FASTLIB',
     &     STATUS='UNKNOWN',
     &     ACCESS='SEQUENTIAL', FORM='UNFORMATTED', IOSTAT=IOS)
                  ENDIF   
     READ (66,IOSTAT=IOS)   LNOW,(WORK(I),I=1,LNOW)


FILE文件的二进制和十进制文件我用这个远古程序附带的小工具在LINUX下转换出来了,也一起上传上来。




原程序.png (57.15 KB, 下载次数: 415)

原程序.png

2.png (104.42 KB, 下载次数: 396)

2.png

十进制文件.txt

2.07 KB, 下载次数: 5

二进制文件.txt

632 Bytes, 下载次数: 7

读取的名为FASTLIB文件


作者: evanustc    时间: 2020-5-15 22:15
初来乍到,论坛好多地方还没有用熟悉
作者: evanustc    时间: 2020-5-15 22:18
附加我自己按照这个程序思想,仿造写的一个类似的。

自己写的程序执行后.png (130.42 KB, 下载次数: 363)

自己写的程序执行后.png

作者: evanustc    时间: 2020-5-16 10:38
十进制文件中的第一行去掉后,与二进制文件是对应的。
作者: necrohan    时间: 2020-5-16 12:12
没有声明默认是IN规则,LNOW是整数。
读入那里应该是先读入整数LNOW,然后读入数组WORK,WORK有LNOW个数据,默认是浮点数。
作者: li913    时间: 2020-5-16 12:53
本帖最后由 li913 于 2020-5-16 13:05 编辑

1、LNOW是什么类型,因为前面也没有显示声明.
如果代码中没有implicit none, 默认用I-N规则解析变量类型,那么就是整型。
2、WORK在前面已经申明是WORK(1)
work是形参数组,其长度并不是1,详细介绍见《三种数组传递方式》http://fcode.cn/guide-103-1.html
这里(WORK(I),I=1,LNOW)是一个隐循环,把Lnow个数据读入work中。
3、读取结果不符合预期,检查文件内容是否正确,文件路径是否正确。
4、 二进制文件,必须知道其存储格式才能读取。二进制和十进制文件转换小工具,这个工具的源代码是重要的线索。
5、你给的这个二进制文件可能有问题,缺少最后一个数据。

1.png (104.24 KB, 下载次数: 382)

1.png

作者: evanustc    时间: 2020-5-16 19:08
necrohan 发表于 2020-5-16 12:12
没有声明默认是IN规则,LNOW是整数。
读入那里应该是先读入整数LNOW,然后读入数组WORK,WORK有LNOW个数据 ...

谢谢!应该是这样执行的!
作者: evanustc    时间: 2020-5-16 19:24
本帖最后由 evanustc 于 2020-5-16 19:26 编辑
li913 发表于 2020-5-16 12:53
1、LNOW是什么类型,因为前面也没有显示声明.
如果代码中没有implicit none, 默认用I-N规则解析变量类型, ...

[attach]2707[/attach]李老师,谢谢您的指点,我下午又补习了下这块的知识,收获颇多。
对于您的这个截图,我按照您的代码试了下,和您的结果吻合。只是我还有几个问题:
1、我原来贴的图中(F77远古代码),我在win下编译没问题,跟到READ(UNIT=IOPDS,ERR=300,IOSTAT=IOS)  LNOW,(WORK(I),I=1,LNOW)后,变量显示LNOW是0,WORK(I)为0,感觉就是WORK没有读进去, 不知道是什么原因,在UBUNTU下编译后,程序是能够正常计算有结果的,也就是说WORK里面读入了数据,我想不明白,路径也没有问题,文件也是同一个二进制文件。
2、您写的这个代码里,第一输出的是0,这个对应10进制文件的哪一个呢?
3、二进制和十进制文件都是通过小程序转换出来的,然后通过VM从UBUNTU拷贝到WIN下,应该是没问题的,而且用二进制转十进制也是能对上的。
我下午正在琢磨小程序,里面关于PDS的读入是这样的,如图,似乎和主程序的套路是一样的,估计我跟到这边,变量显示还是会有同样的问题。

小程序中PDS读写函数.png (82.96 KB, 下载次数: 356)

小程序中PDS读写函数.png

作者: necrohan    时间: 2020-5-16 19:56
要先把 LNOW 读对才能解决后面的问题
作者: evanustc    时间: 2020-5-16 21:10
li913 发表于 2020-5-16 12:53
1、LNOW是什么类型,因为前面也没有显示声明.
如果代码中没有implicit none, 默认用I-N规则解析变量类型, ...

我晚上用您的这个程序改写为77后,又读了其他二进制的文件,和小工具在UBUNTU下运行转换出来的结果对比,区别:1、用小工具转换二进制和十进制互转可以对上。
1、自己转的,相比于十进制文件,我的前面总是多个0并且文件最后少一个数据(和您运行的结果得到的结论一致,但是相同的二进制文件用自带工具转十进制就没问题)。
2、我上传了转10进制的小工具,和CSH执行脚本。

希望得到您的帮助,可能会麻烦您,我可以给您付一点费用或者我捐赠下论坛发展,虽然我还是个学生

codesource.zip

89.49 KB, 下载次数: 61

二进制转十进制小工具,UBUNTU下编译可直接运行


作者: li913    时间: 2020-5-17 09:19
本帖最后由 li913 于 2020-5-17 09:40 编辑

你静态编译一个小工具的可执行,附带二进制文件,以及使用方法, 一起上传。
之前的二进制文件肯定是有问题的。
作者: evanustc    时间: 2020-5-17 10:48
li913 发表于 2020-5-17 09:19
你静态编译一个小工具的可执行,附带二进制文件,以及使用方法, 一起上传。
之前的二进制文件肯定是有问题 ...

李老师,感谢!已上传!

二进制转十进制工具.rar

21.37 KB, 下载次数: 4

使用说明1.rar

473.24 KB, 下载次数: 3

使用说明2.rar

874.45 KB, 下载次数: 132


作者: evanustc    时间: 2020-5-17 10:49
还有一个所有二进制合并成的一个十进制文件,50多M,上传不上来,已在说明中截图示意。
作者: evanustc    时间: 2020-5-17 11:22
li913 发表于 2020-5-17 09:19
你静态编译一个小工具的可执行,附带二进制文件,以及使用方法, 一起上传。
之前的二进制文件肯定是有问题 ...

李老师,问题似乎解决部分了。如您说,二进制文件有问题,我从服务器重新下载了FASTLIB后,重新用自己写的F77:
leng为153,后面的work读入了153个数据,但是74 10 22 45这4个读入后显示的为小数*E-43这种,是因为我声明的是REAL WORK吧,但是我按照老程序把WOK改写为DIMENSION后,结果不变。
DIMENSION WORK(1)
OPEN(UNIT=IOPDS,FILE=FILNAM(1:LNFILE),ERR=100, STATUS='UNKNOWN',
     &     ACCESS='SEQUENTIAL', FORM='UNFORMATTED',IOSTAT=IOS)
                  ENDIF
       READ(UNIT=IOPDS,ERR=300,IOSTAT=IOS)  LNOW,(WORK(I),I=1,LNOW)  
然后我重新运行老程序,得到结果如下,由于WORK(1)读入的居然是1个值,和我自己写的读入后work(1)相同,这种work(1)的声明方式,并没有预想的读入153个数据,然后前面4个数据也非整形我想要的,这个我该如何解决呢?似乎小程序的运行暂时可以放下了。

123.jpg (288.75 KB, 下载次数: 217)

自己的

自己的

345.jpg (143.95 KB, 下载次数: 195)

老程序结果

老程序结果

作者: evanustc    时间: 2020-5-17 11:25
有点激动,整了快2天的时间,对Fortran理解不少,太有意思了
作者: evanustc    时间: 2020-5-17 11:34
我把 DIMENSION WORK(300)改为INTEGER  WORK(300) 能够读入74 10 22 45但是后面小数就不行了,读的全是整数。如果WORK为REAL型,为啥不能把74当做74.0读入呢?类似C++里面的读入处理
作者: necrohan    时间: 2020-5-17 12:49
evanustc 发表于 2020-5-17 11:34
我把 DIMENSION WORK(300)改为INTEGER  WORK(300) 能够读入74 10 22 45但是后面小数就不行了,读的全是整数 ...

整数和浮点数在二进制文件里表示方法不一样,不能通用,否则读出来数据是错的。
你附件那个二进制文件按6楼的方式读没问题。
作者: evanustc    时间: 2020-5-17 13:42
necrohan 发表于 2020-5-17 12:49
整数和浮点数在二进制文件里表示方法不一样,不能通用,否则读出来数据是错的。
你附件那个二进制文件按6 ...

是的,二进制读入没有问题。我明白您的意思,只是有点疑惑的就是小工具中关于二进制代码读取的部分:
      DIMENSION WORK(1)
      OPEN(UNIT=IOPDS, FILE=FILNAM, ERR=100, STATUS='UNKNOWN',
     &     ACCESS='SEQUENTIAL', FORM='UNFORMATTED',IOSTAT=IOS)
      READ(UNIT=IOPDS,ERR=300,IOSTAT=IOS)  LENG,(WORK(I),I=1,LENG)
      CLOSE(UNIT=IOPDS, ERR=200, STATUS='KEEP',IOSTAT=IOS)
      RETURN74
WORK声明的是数组,也没有指定类型,在UBUNTU下编译后,转出的10进制文件是正确的。我推断WORK中即读入了整型也有实型,不然打印成十进制不可能对的上。这部分我在win下实现后就出现了我刚才问的问题,屏幕输出的前4个本应该是74 10 22 45却变为了神秘小数。。
作者: li913    时间: 2020-5-17 16:13
本帖最后由 li913 于 2020-5-17 17:51 编辑

研究了两天,发现这个代码是比较奇葩的,典型的国外开源代码风格,故意让人看不懂(也可能是代码太老了)。核心也就那么十来行。将所有数据读入数组,通过一定规则(numchk函数)判断每个数据的真实类型(real 或 integer),然后每6个数据输出一行。

[Fortran] 纯文本查看 复制代码
!判断数据是整数还是实数
subroutine numchk (ia,icode)
character aho*7
write(aho,'(i7)') ia
if(aho.eq.'*******') then
  icode = 0
else
  icode = 1
endif
end
      
program test
implicit none
integer(4) m, n, i, j, k
real,allocatable:: a(:)
real b
equivalence(b,n)
character cl*3

!读取
open(10,file='FASTLIB', ACCESS='SEQUENTIAL',FORM='UNFORMATTED')
read(10) m
allocate(a(m)) !分配空间
rewind(10)
read(10) m, a(1:m)
close(10)

!输出
open(10,file='out.txt')
k=0
do i = 1, m
  b = a(i)
  k=k+1
  if(mod(k,6)==0) then !每6个数据输出一行
    cl = 'yes'
  else
    cl = 'no'
  end if
  call numchk(n,j) !判断数据是整数还是实数
  if(j==0) then
    write(10,'(1pe12.5)', advance=cl) b
  else
    write(10,'(4x,i7,1x)',advance=cl) n
  end if
end do
end

QQ截图20200517160850.png (106.58 KB, 下载次数: 299)

QQ截图20200517160850.png

1.png (81.34 KB, 下载次数: 278)

1.png

作者: evanustc    时间: 2020-5-17 19:57
本帖最后由 evanustc 于 2020-5-17 20:04 编辑
li913 发表于 2020-5-17 16:13
研究了两天,发现这个代码是比较奇葩的,典型的国外开源代码风格,故意让人看不懂(也可能是代码太老了)。 ...

李老师,请收下我的膝盖orz....太厉害了。在结合工具代码的基础上,阅读您的代码,我明白了意思。在您的代码里,有一项我不太明白原理,就是a(1)读进来的时候还是REAL型的小数,但是在最后的循环中,使用了 b=a(1),然后整形n就成了74了。。。  equivalence(b,n)这一句,实型b和整型n共用同一块内存地址,是由于整型和实型的原因导致取同一块地址里的值时导致不一样的吗?为啥不用n=b转换呢
作者: evanustc    时间: 2020-5-17 20:10
我思考了下,是因为共用一块地址,地址里的值是二进制的,如010110,那么分别用不同类型的变量取值这段二进制的值时,就会翻译为相应的数据类型值。而n=b这种是数据上的强制转换
作者: li913    时间: 2020-5-17 20:15
1、equivalence(b,n), 二者的二进制表示形式一样(同一块内存),但值不一样;
2、n=b, 值一样(或近似,存在取舍), 二进制不一样。
3、文件中的某个地址上的值,到底是整型还是实型,需要通过判断才能确定,但不管是啥类型,二进制形式是相同的(文件不会改变)。
4、参考 http://fcode.cn/guide-120-1.html
作者: evanustc    时间: 2020-5-19 10:21
li913 发表于 2020-5-17 20:15
1、equivalence(b,n), 二者的二进制表示形式一样(同一块内存),但值不一样;
2、n=b, 值一样(或近似,存在 ...

谢谢李老师,彻底明白了!感谢您的指点!继续前行,加油!




欢迎光临 Fortran Coder (http://bbs.fcode.cn/) Powered by Discuz! X3.2