weixing1531 发表于 2022-10-26 13:50:05

Fortran编程技巧(持续更新)

本帖最后由 weixing1531 于 2023-3-12 21:31 编辑

学习Fortran已有近20年,收集了一些编程技巧。
(1)修改老程序goto语句
先构造一个只执行一次的命名循环,再使用exit退出命名循环从而避免使用goto语句
老程序
if(...)then
语句A
goto 99
end if
if(...)then
语句B
goto 99
end if
语句C
99 语句D
等价于新程序
out:do i=1,1
if(...)then
    语句A
    exit out
end if
if(...)then
    语句B
    exit out
end if
语句C
end do out
语句D
(2)整数转换为字符串
!整数转换为字符串
pure function int2str(i) result(f_string) !摘自fpm源代码
    integer,intent(in)::i !i为要转换的整数
    character(len=str_int_len(i))::f_string !自动字符串 调用已有函数str_int_len 7.3Automatic objects《Mordern Fortran Explained 2018》P120
   
    write(f_string,"(i0)")i !f_string长度必须明确
end function int2str
!> Returns the length of the string representation of 'i'
pure integer function str_int_len(i) result(sz) !摘自fpm源代码
    integer, intent(in) :: i
    integer, parameter :: MAX_STR = 255 !字符串最大长度
    character(MAX_STR) :: s
    ! If 's' is too short (MAX_STR too small), Fortran will abort with:
    ! "Fortran runtime error: End of record"
    write(s, '(i0)') i !左对齐 F202X标准才支持s为递延字符串
    sz = len_trim(s) !删除尾部空格后的字符串长度
end function str_int_len
(3)浮点数转换为字符串pure function real2str(MXS,ld) result(res) !浮点数转换为字符串 调用str_int_len、int2str函数
    real(real64),intent(in)::MXS !浮点数
    integer,intent(in)::ld !小数保留位数
    character(len=:),allocatable::res,ft !递延长度字符串
    character(len=255)::str
    integer::i
   
    i=nint(MXS) !浮点数四舍五入取整 避免重复计算
   
    select case(ld)
      case(0) !浮点数四舍五入取整
            res=int2str(i) !不需要ft
      case(1:4) !小数保留1-4位
            ft="(f"//int2str(str_int_len(i)+ld+1)//"."//int2str(ld)//")" !"(fX.Y)" 浮点数格式 Y为小数点保留位数 X为总字符串长度(整数部分+小数点+小数部分)
            write(str,ft)MXS !浮点数转换为字符串 左对齐 只能用str而不能用res F202X标准才支持str为递延字符串
            res=trim(str) !删除尾部空格
            deallocate(ft) !释放递延字符串内存 不能放在函数尾部 否则浮点数四舍五入取整无法计算
      case default
            error stop "小数保留位数超出范围!"
    end select
end function real2str
(4)C字符串指针转换Fortran字符串
FUNCTION C_to_F_string(c_string_pointer) RESULT(f_string) !C字符串指针转换Fortran字符串
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_CHAR, C_NULL_CHAR
      type(C_PTR), INTENT(IN) :: c_string_pointer !C字符串指针
      CHARACTER(LEN=:), ALLOCATABLE :: f_string !延迟长度字符串
      CHARACTER(KIND=C_CHAR), DIMENSION(:), POINTER :: char_array_pointer => NULL() !Fortran字符数组指针
      CHARACTER(LEN=255) :: aux_string
      INTEGER :: i, length=0

      CALL C_F_POINTER(c_string_pointer, char_array_pointer, ) !C字符串指针转换为Fortran字符数组指针

      IF (.NOT.ASSOCIATED(char_array_pointer)) THEN !char_array_pointer为空指针
            ALLOCATE(CHARACTER(LEN=4)::f_string)
            f_string="NULL"
            return
      END IF

      aux_string=" "

      DO i=1,255
            IF (char_array_pointer(i)==c_null_char) THEN !达到字符串尾部
                length=i-1
                EXIT
            END IF

            aux_string(i:i)=char_array_pointer(i) !逐个元素复制字符
      END DO

      ALLOCATE(CHARACTER(LEN=length)::f_string)
      f_string=aux_string(1:length)
END FUNCTION C_to_F_string
(5)读写Excel数据
方法一:商业库Libxl提供了Fortran函数接口,序列号需要破解
https://www.libxl.com/
方法二:先读写csv文件,然后手动另存为xls格式
Github网上有现成的csv模块,作者:Jacob Williams
(6)日期与时间操作
Github网上有现成的datetime模块
https://github.com/wavebitscientific/datetime-fortran
(7)常用数值计算算法原代码
Numerical Recipe、宋叶志、何光渝、徐士良
(8)生成随着时间与日期变动的动态密码
impure function GetPassword(PasswordLen,IsDay) !获得动态密码及长度
    integer,intent(out)::PasswordLen
    logical,intent(in),optional::IsDay !动态密码是否日变化
    character(len=:),allocatable::GetPassword !递延长度字符串
    character(8):: date,temp !CCYYMMDD
    character(10) :: time !HHMMSS.SSS
   
    call date_and_time(date,time) !返回当前日期与时间
    temp=date(3:8)//time(1:2) !界面进入原始密码 为与时间有关的动态密码YYMMDDHH 两位年月日时
   
    if(present(IsDay))then
      if(IsDay)temp=date(1:8) !界面进入密码 为与日期有关的动态密码CCYYMMDD 四位年月日
    end if
   
    GetPassword="Wx"
    GetPassword=GetPassword//temp(2:8)//temp(1:1) !加工后密码
    PasswordLen=len(GetPassword) !输出密码字符串长度
end function
(9)判断整数变量b的值是否与一维整数数组a的任意元素相等
方法一:
Any(a(:)==b) !T相等F不相等
方法二:
FindLoc(a(:),b)== !F相等T不相等(10)如何判断两个字符串完全相等
character(:),allocatable::a,b
a="A"
b="A " !A后面加一个空格
write(*,*)a==b !你以为打印F 其实打印T
write(*,*)(a==b .AND. (len(a)==len(b))) !打印F
(11)如何在主程序中修改模块protected属性变量?将该变量修改包裹在模块方法之中,然后在主程序调用模块方法
(12)常用参考网站
https://fortranwiki.org/fortran/show/HomePage
https://fortran-lang.org/
https://jblevins.org/mirror/amiller/











fcode 发表于 2022-10-28 08:53:15

楼主这个帖子主题不错。我也来一个技巧。

善用常量数组来替代一些 if else。
比如,经典的考试题目,星期一看电影,星期二看书,星期三聚会,星期四野营,星期五星期六旅行,星期日休息。
输入星期几,输出要做的事情。

普通代码:
program fcode_cn
integer :: wk
read(*,*) wk
select case(wk)
case (1)
    write(*,*) "看电影"
case (2)
    write(*,*) "看书"
case (3)
    write(*,*) "聚会"
case (4)
    write(*,*) "野营"
case (5,6)
    write(*,*) "旅行"
case (7)
    write(*,*) "休息"
end select
end program fcode_cn

利用常量数组代码:
program fcode_cn
character(len=6) , parameter :: ACT(*) = &
   
integer :: wk
read(*,*) wk
write(*,*) ACT(wk)
end program fcode_cn

可以看到,合理的使用常量数组,不但代码简练,并且易于维护,以后增加和修改都更简单。
同时,也便与遍历整个星期。
do i = 1 , size(ACT)
    write(*,*) ACT(i)
end do

oleg 发表于 2022-11-11 02:22:48

fcode 发表于 2022-10-28 08:53
楼主这个帖子主题不错。我也来一个技巧。

善用常量数组来替代一些 if else。


学习了 这个方法真的好用

gps99 发表于 2024-2-28 12:56:00

楼主几个例子很赞,自己造轮子.

也是猛抽Fortran的老脸。。:-Q:-Q
页: [1]
查看完整版本: Fortran编程技巧(持续更新)