【问题标题】:Is there an alternative to GETCWD() in Fortran 2003-2008Fortran 2003-2008 中是否有 GETCWD() 的替代方法
【发布时间】:2015-05-16 18:24:27
【问题描述】:

GNU Fortran 编译器的 GNU 扩展提供了很好的子例程 GETCWD(),获取当前工作目录。但是,我的代码也必须可移植到 ifortnagfor 编译器,并且我使用 F2003 功能。

那么,对于 F2003 及更高版本,GETCWD() 是否有替代方案?

我这里有标准,但它相当大,我已经通过它一段时间了,还没有发现任何有用的东西......

【问题讨论】:

    标签: fortran gfortran fortran2003 fortran2008


    【解决方案1】:

    如 cmets 中所述,您可以使用标准 Fortran 的 get_environment_variable(例如 F2008 13.7.67)。此示例程序查询$PWD 的值,该值应包含您调用可执行文件时shell 所在的目录。

    program test
     implicit none
     character(len=128) :: pwd
     call get_environment_variable('PWD',pwd)
     print *, "The current working directory is: ",trim(pwd)
    end program
    

    及其输出:

    casey@convect code % pwd
    /home/casey/code
    casey@convect code % so/getpwd 
     The current working directory is: /home/casey/code
    

    这是标准的 Fortran,但它的可移植性仅限于设置此变量的 Unix 和类 Unix shell。

    另一个标准但丑陋的选择(在我看来)是使用execute_command_line 运行可以将工作目录输出到临时文件(例如pwd > /tmp/mypwd)的命令,然后读取该文件。

    【讨论】:

    • PWD不适用于windows,需要查询CD
    • 谢谢凯西! :) 因为在这种情况下可移植性绝对是一个问题,所以我接受了 Alexander 的回答,但你的回答肯定会对我和其他人将来有用。
    【解决方案2】:

    也可以使用ISO_C_Binding,调用对应的C函数:

    cwd.c:

    #ifdef _WIN32
    /* Windows */
    #include <direct.h>
    #define GETCWD _getcwd
    
    #else
    /* Unix */
    #include <unistd.h>
    #define GETCWD getcwd
    
    #endif
    
    void getCurrentWorkDir( char *str, int *stat )
    {
      if ( GETCWD(str, sizeof(str)) == str ) {
        *stat = 0;
      } else {
        *stat = 1;
      }
    }
    

    测试.F90:

    program test
     use ISO_C_Binding, only: C_CHAR, C_INT
     interface
       subroutine getCurrentWorkDir(str, stat) bind(C, name="getCurrentWorkDir")
         use ISO_C_Binding, only: C_CHAR, C_INT
         character(kind=C_CHAR),intent(out) :: str(*)
         integer(C_INT),intent(out)         :: stat
        end subroutine
      end interface
      character(len=30)   :: str
      integer(C_INT)      :: stat
    
      str=''
      call getCurrentWorkDir(str, stat)
      print *, stat, trim(str)
    
    end program
    

    此代码适用于 Windows 和 Unix 衍生版本(Linux、OSX、BSD 等)

    【讨论】:

    • 感谢您的全面回答! :) 我对与 C 的接口缺乏经验,但会尝试。如果我说这会导致我的 makefile 变得稍微复杂一些,我是否正确,因为我需要为 C 定义一个编译器?
    • 这取决于...为了编译这个例子,我只是使用了gfortran -Wall -Wextra -g cwd.c test.F90
    【解决方案3】:

    接受的答案包含两个错误(它将错误的值作为字符串长度传递给GETCWD,并留在C_NULL_CHAR)。这个答案纠正了这些错误,并使界面在 Fortran 中更有用。

    基本思想是相同的:使用 C 调用 getcwd_getcwd,并使用 Fortran 的 C 互操作性特性调用 C 包装器。在 Fortran 方面,包装子程序用于处理字符串长度,因此不必显式传递。

    另外,C_INTC_CHAR 不必与 Fortran 端需要的默认整数和默认字符相同(尽管实际上我不知道有任何系统 C_CHAR 和默认字符不同)。包装器也会转换那些。此外,从 C 返回的字符串包含终止符 C_NULL_CHAR,必须删除它才能使字符串在 Fortran 端可用。

    C 代码:

    #ifdef _WIN32
    #include <direct.h>
    #define GETCWD _getcwd
    #else
    #include <unistd.h>
    #define GETCWD getcwd
    #endif
    
    /* Return 0 on success, 1 on error. */
    int getCWDHelper(char *str, int len)
    {
        return GETCWD(str, len) != str;
    }
    

    Fortran 代码:

    module cwd
        use iso_c_binding, only: C_INT, C_CHAR, C_NULL_CHAR
        implicit none
        private
        public :: getCWD
    
        interface
            function getCWDHelper(str, len) bind(C, name="getCWDHelper")
                use iso_c_binding, only: C_INT, C_CHAR
                integer(kind=C_INT) :: getCWDHelper
                character(kind=C_CHAR), intent(out) :: str(*)
                integer(kind=C_INT), value :: len
            end function getCWDHelper
        end interface
    
    contains
    
        ! Writes the current working directory path into str.
        ! Returns 0 on success, or 1 on error.
        function getCWD(str)
            integer :: getCWD
            character(*), intent(out) :: str
    
            integer :: i, length
            character(len=len(str), kind=C_CHAR) :: str_copy
    
            ! Call the C helper, passing the length as the correct int kind
            getCWD = getCWDHelper(str_copy, len(str_copy, kind=C_INT))
    
            if (getCWD /= 0) then
                str = '' ! Error, clear the string
                return
            end if
    
            ! Copy the C_CHAR string to the output string,
            ! removing the C_NULL_CHAR and clearing the rest.
            length = index(str_copy, C_NULL_CHAR) - 1
            do i = 1, length
                str(i:i) = char(ichar(str_copy(i:i)))
            end do
            str(length+1:) = ''
        end function getCWD
    
    end module
    

    测试代码:

    program test
        use cwd, only: getCWD
        implicit none
    
        character(len=255) :: path
        integer :: error
    
        error = getCWD(path)
    
        print *, error
        if (error == 0) print *, path
    end program
    

    使返回值可分配并循环以获得足够的大小留给读者作为练习。

    【讨论】:

    • 非常感谢。这就像一个魅力。
    猜你喜欢
    • 2013-11-19
    • 1970-01-01
    • 2021-11-06
    • 2023-03-21
    • 2013-05-18
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 1970-01-01
    相关资源
    最近更新 更多