You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Fortran中读取文件除最后两行外的所有内容?

嘿,这个问题问到点子上了!你现在的代码确实要遍历文件两次——先数行数再读内容,这在处理大量文件的时候,IO开销会非常大。其实完全不用先统计总行数,有好几种更高效的办法,我给你拆解一下:

方法1:内存足够时优先用——读全量再截掉最后两行

如果你的文件不是特别大,内存能轻松装下所有内容,这个方法是最直接高效的,只需要一次文件遍历,比你原来的两次IO快太多:

DO I = 1, 1000
    OPEN(UNIT=I, FILE=FILENAME(I), STATUS='OLD', ACTION='READ')
    
    INTEGER :: io, line_num = 0
    ! 先初始化一个动态数组,不够了再自动扩容
    REAL, ALLOCATABLE :: all_content(:)
    ALLOCATE(all_content(1000)) ! 先给个初始大小,根据你的需求调整
    
    DO
        line_num = line_num + 1
        ! 数组不够大就扩容
        IF (line_num > SIZE(all_content)) THEN
            REAL, ALLOCATABLE :: temp(:)
            ALLOCATE(temp(SIZE(all_content)*2))
            temp(1:SIZE(all_content)) = all_content
            DEALLOCATE(all_content)
            CALL MOVE_ALLOC(temp, all_content)
        END IF
        READ(I, *, IOSTAT=io) all_content(line_num)
        IF (io /= 0) THEN
            line_num = line_num - 1 ! 最后一次循环没读到内容,回退计数
            EXIT
        END IF
    END DO
    
    ! 处理文件行数少于2的情况(没内容要保留)
    IF (line_num <= 2) THEN
        DEALLOCATE(all_content)
        CLOSE(I)
        CYCLE
    END IF
    
    ! 把前line_num-2行存到你的A数组里
    A = all_content(1:line_num-2)
    DEALLOCATE(all_content)
    
    CLOSE(I)
END DO

这个思路的核心就是:把所有内容一次性读进内存,再做截断——毕竟内存操作比磁盘IO快几个数量级,能省一次磁盘遍历就省一次。

方法2:内存不够?用反向定位跳过最后两行

如果文件太大装不下内存,那就用BACKSPACE操作定位到倒数第三行的开头,然后从那里开始读所有内容,避免第二次完整遍历:

DO I = 1, 1000
    OPEN(UNIT=I, FILE=FILENAME(I), STATUS='OLD', ACTION='READ')
    
    INTEGER :: io, line_count = 0
    ! 先读到文件末尾,顺便数下行数(也可以不数,但要处理行数不足的情况)
    DO
        READ(I, *, IOSTAT=io)
        IF (io /= 0) EXIT
        line_count = line_count + 1
    END DO
    
    ! 文件行数不够2,直接跳过
    IF (line_count <= 2) THEN
        CLOSE(I)
        CYCLE
    END IF
    
    ! 回退两次,回到倒数第三行的开头
    BACKSPACE(I, IOSTAT=io)
    IF (io /= 0) THEN ! 处理回退失败的情况,比如文件损坏
        CLOSE(I)
        CYCLE
    END IF
    BACKSPACE(I, IOSTAT=io)
    IF (io /= 0) THEN
        CLOSE(I)
        CYCLE
    END IF
    
    ! 现在从当前位置读取所有内容,直到文件末尾
    INTEGER :: j = 0
    DO
        j = j + 1
        READ(I, *, IOSTAT=io) A(j)
        IF (io /= 0) EXIT
    END DO
    
    CLOSE(I)
END DO

这里的BACKSPACE是磁盘上的定位操作,比逐行读取快很多,所以比你原来的两次完整遍历效率高不少。

方法3:固定记录长度文件专属——直接定位读取

如果你的文件是固定记录长度的(比如二进制文件,或者格式化文件每行长度完全一致),那可以用直接访问模式,直接通过INQUIRE获取总行数,然后读取前n-2行,完全不需要遍历文件:

DO I = 1, 1000
    INTEGER :: recl_len
    ! 先获取文件的记录长度
    INQUIRE(FILE=FILENAME(I), RECL=recl_len)
    
    ! 用直接访问模式打开文件
    OPEN(UNIT=I, FILE=FILENAME(I), STATUS='OLD', ACTION='READ', ACCESS='DIRECT', RECL=recl_len)
    
    INTEGER :: total_lines
    ! 直接获取总记录数(行数)
    INQUIRE(UNIT=I, NUMBER=total_lines)
    
    IF (total_lines <= 2) THEN
        CLOSE(I)
        CYCLE
    END IF
    
    ! 直接读取前total_lines-2行
    DO J = 1, total_lines-2
        READ(I, REC=J) A(J)
    END DO
    
    CLOSE(I)
END DO

这个方法效率最高,但只适用于固定记录长度的文件,格式化文件如果每行长度不一样的话没法用。

总结一下优先级:

  1. 内存够→用方法1(代码简单,IO最少)
  2. 内存不够→用方法2(比原方法少一次完整IO)
  3. 固定记录长度文件→用方法3(效率天花板)

内容的提问来源于stack exchange,提问作者idladle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:27:38