Fortran Coder

标题: 将文本文件转化为二进制文件,然后再转出来 [打印本页]

作者: 肖邦的SK    时间: 2017-10-30 12:14
标题: 将文本文件转化为二进制文件,然后再转出来
小弟最近在做一个文本文件转化为二进制文件的问题,希望可以将文件的大小实现压缩,然后需要的时候再转为文本文件读取,现在这个文件是这样的,就是前面的19行均为长度不一得字符串,后面是几行小数,具体小数的行数就是第19行字符串中的整数,如此循环,我编了一个程序,编译没什么问题,但是一运行就卡着半天不动了,不知道问题出在哪了,我的思路是想先把前面19行循环读取,然后再把第19行的字符串转化为整数,然后循环读取小数,但是这样的话出现很多空格,文件反而更大了,希望各位大佬能不吝赐教。
[Fortran] 纯文本查看 复制代码
program jack01
    implicit none
   
    character(len = 66) buffer
    integer :: num = 0
    real(kind = 4) :: a, b
    integer :: status = 0
    integer countera,counterb
    integer, parameter :: lines = 19
    integer :: r = 1
    logical alive
   
    inquire(file = 'new.txt',exist = alive)
    if(alive)then
        !打开输入输出文件
        open(unit = 12, file = 'new.txt')
        open(unit = 13, file = 't.bin', access = 'direct', form = 'unformatted', recl = 20 )
        do while(.true.)
            !转存前面19行的内容
            do  countera = 1, lines, 1
                read(14, fmt = "(A66)",iostat = status) buffer
                if(status /= 0) exit
                write(15, rec = r) buffer
                r = r + 1
            end do
            countera = 1
           
            !转存实数部分的内容
            do  counterb = 1, num, 1
                read(14, *, iostat = status) a, b
                if(status /= 0) exit
                write(15, rec = r) a, b
                r = r + 1
            end do
            counterb = 1
        end do
    else
        write(*,*)'new.txt',"does not exist"
    end if
   
    !关闭输入输出文件
    close(12)
    close(13)
    stop
    end

new.txt

61.56 KB, 下载次数: 10


作者: pasuka    时间: 2017-10-30 12:34
为啥不能先把所有的txt文件打包成单个zip、gz格式的压缩文件,然后按照规则抽取需要的txt文件呢?
作者: 楚香饭    时间: 2017-10-30 12:46
本帖最后由 楚香饭 于 2017-10-30 12:53 编辑

你这种问题不太容易用二进制存储,因为每个 block 的文本头部都不一样长。
后面用二进制读取的时候,不知道读多少文本头部。

如果你非要转二进制,看下面代码的修改

[Fortran] 纯文本查看 复制代码
program jack01
  implicit none
  character(len = 66) buffer
  integer :: num = 0
  real(kind = 4) :: a, b
  integer :: status = 0
  integer i
  integer, parameter :: lines = 18 !//文本只算18行
  logical alive

  inquire(file = 'new.txt',exist = alive)
  if(.not.alive) then
    write(*,*) "new.txt does not exist"
    stop
  end if
  !打开输入输出文件
  open(unit = 14, file = 'new.txt')
  open(unit = 15, file = 't.bin', access = 'stream')!//stream是一个很霸道的二进制读写方式
CY: Do  !//命名循环
    !转存前面19行的内容
    do  i = 1, lines
      read(14, fmt = "(A66)",iostat = status) buffer
      if(status /= 0) exit CY
      write(15) trim(buffer) !//trim一下以便减小尺寸
    end do
    read(14,*,iostat=status) num !//获得行数
    if(status /= 0) exit CY
    !转存实数部分的内容
    do  i = 1, num
      read(14, *, iostat = status) a, b
      if(status /= 0) exit CY
      write(15) a, b
    end do
  End Do CY
  close(12)
  close(13)  
end program jack01


作者: 肖邦的SK    时间: 2017-10-30 14:31
pasuka 发表于 2017-10-30 12:34
为啥不能先把所有的txt文件打包成单个zip、gz格式的压缩文件,然后按照规则抽取需要的txt文件呢? ...

导师要求的,要我那这个先练习练习,然而一直报错
作者: pasuka    时间: 2017-10-30 14:37
肖邦的SK 发表于 2017-10-30 14:31
导师要求的,要我那这个先练习练习,然而一直报错

gzip就是一种无损压缩的二进制存储格式
https://en.wikipedia.org/wiki/Gzip
https://en.wikipedia.org/wiki/DEFLATE
若是配合Python,处理起来很方便
https://docs.python.org/3/library/gzip.html#module-gzip

作者: 肖邦的SK    时间: 2017-10-30 16:36
楚香饭 发表于 2017-10-30 12:46
你这种问题不太容易用二进制存储,因为每个 block 的文本头部都不一样长。
后面用二进制读取的时候,不知道 ...

万分感谢,是可以转出来了。不过就是转换回去的时候如果用流文件的处理是要读取指定字节数的,但是这边的头文件区域的字节数都是不固定的,所以之前我在一开始转的时候想把他弄成一条条的记录去做,然后转成文本文件的时候直接一条条的读,但是好像这样的话弄出来的二进制文件反而更大了。
作者: 肖邦的SK    时间: 2017-10-30 16:38
pasuka 发表于 2017-10-30 14:37
gzip就是一种无损压缩的二进制存储格式
https://en.wikipedia.org/wiki/Gzip
https://en.wikipedia.org/wi ...

谢谢帮助,这个很有用。我会继续学习的,因为现在是刚学习Fortran阶段,所以老师也是希望我通过这个练习熟悉熟悉Fortran语言,可能后续的课题也会使用。
作者: 楚香饭    时间: 2017-10-31 08:36
本帖最后由 楚香饭 于 2017-10-31 08:42 编辑

可以把文本的大小也写在二进制文件里。

以下代码在windows上运行。如果要在其他操作系统上运行,建议修改 crlf 的定义。

[Fortran] 纯文本查看 复制代码
program jack02
  implicit none
  integer :: status , n , i
  Real :: a , b
  character(len=1) , allocatable :: text(:)
  open(unit = 15, file = 't.bin', access = 'stream')!//stream是一个很霸道的二进制读写方式
  open(unit = 14, file = 'newback.txt')
  Do
    Read(15,iostat=status) n !//得到文本大小
    if(status/= 0) exit
    if(n==0) exit
    Allocate(text(n))
    Read(15) text
    Write(14,'(a,\)') text
    Deallocate(text)
    Read(15,iostat=status) n !//得到二进制大小
    if(status/= 0) exit
    write(14,*) n
    Do i = 1 , n
      Read(15) a , b
      Write(14,*) a , b
    End Do
  End Do
End Program jack02


[Fortran] 纯文本查看 复制代码
program jack01
  implicit none
  character(len = 66) buffer
  integer :: num = 0
  real(kind = 4) :: a, b
  integer :: status = 0
  integer i , locBegin , locEnd
  integer, parameter :: lines = 18 !//文本只算18行
  character(len=*) , parameter :: crlf = char(z'd') // char(z'a')!//换行符,不同操作系统不一样
  logical alive

  inquire(file = 'new.txt',exist = alive)
  if(.not.alive) then
    write(*,*) "new.txt does not exist"
    stop
  end if
  !打开输入输出文件
  open(unit = 14, file = 'new.txt')
  open(unit = 15, file = 't.bin', access = 'stream')!//stream是一个很霸道的二进制读写方式
CY: Do  !//命名循环
    !转存前面18行的内容
    write(15) 0 !//先输出一个0,表示大小
    Inquire( 15 , pos = locBegin ) !//查询块开始位置
    do  i = 1, lines
      read(14, fmt = "(A66)",iostat = status) buffer
      if(status /= 0) exit CY
      write(15) trim(buffer) , crlf !//trim一下以便减小尺寸,每行输出换行符
    end do
    Inquire( 15 , pos = locEnd ) !//查询块结束位置
    write(15,pos=locBegin-4) locEnd-locBegin !//在开始前4个字节写入大小
    read(14,*,iostat=status) num !//获得行数
    if(status /= 0) exit CY
    write(15,pos=locEnd) num !//回到块结束,写入实数部分的大小
    !转存实数部分的内容
    do  i = 1, num
      read(14, *, iostat = status) a, b
      if(status /= 0) exit CY
      write(15) a, b
    end do
  End Do CY
  close(12)
  close(13)  
end program jack01



作者: 肖邦的SK    时间: 2017-11-6 09:39
楚香饭 发表于 2017-10-31 08:36
可以把文本的大小也写在二进制文件里。

以下代码在windows上运行。如果要在其他操作系统上运行,建议修改  ...

谢谢您,我拜读了一下您的代码,并且也跑了一下,成功了,但是任然有一些地方不是很明白 character(len = *) , parameter :: crlf = char(z'd') // char(z'a')!//换行符,不同操作系统不一样,这个是指从一个换行符到另外一个换行符的长度吗,但是这个换行符我在书上和网上一直没找到,还有就是 Write(14,'(a,\)') text里面的'(a,\)'我也不是很清楚,查了一些资料但是始终还是没太明白
作者: 楚香饭    时间: 2017-11-6 10:28
换行符的问题,可以百度搜索“crlf”
(a,\) 意思是,输出一个字符(a),并且不换行(\)。
作者: 肖邦的SK    时间: 2018-3-7 14:25
楚香饭 发表于 2017-11-6 10:28
换行符的问题,可以百度搜索“crlf”
(a,\) 意思是,输出一个字符(a),并且不换行(\)。 ...

请问Fortran里面的crlf为什么可以被定义为character(len=*) , parameter :: crlf = char(z'd') // char(z'a')啊,我在网上看了好像是0x0A,0x0D,但是为什么Fortran里面可以这么表示呢?
作者: vvt    时间: 2018-3-7 21:30
网上错了




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