如何在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(代码简单,IO最少)
- 内存不够→用方法2(比原方法少一次完整IO)
- 固定记录长度文件→用方法3(效率天花板)
内容的提问来源于stack exchange,提问作者idladle
相关产品推荐
相关产品推荐

