使用f2py将numpy字符串格式数组传递给fortran

5 投票
3 回答
1696 浏览
提问于 2025-04-17 21:35

我的目标是从一个Python的numpy数组中打印第二个字符串,但我总是只打印出第一个字符,而且这个字符不一定是正确的字符串。

有没有人能告诉我,正确地将完整的字符串数组传递给Fortran的方法是什么?

代码如下:

testpy.py

import numpy as np
import testa4

strvar = np.asarray(['aa','bb','cc'], dtype = np.dtype('a2'))
testa4.testa4(strvar)

testa4.f90

subroutine testa4(strvar)
implicit none

character(len=2), intent(in) :: strvar(3)
!character*2 does not work here - why?

print *, strvar(2)

end subroutine testa4

编译命令是

f2py -c -m testa4 testa4.f90

上面代码的输出

c

期望的输出

bb

3 个回答

0

这不是一个真正的答案,但评论太长了:也许这对你有帮助..

在你的第一个例子中,传给Fortran的原因是每个字符串的第一个字符:

'abc'

在Fortran中,这些字符最终会变成长度为2的数组,内容是'ab'和'c'。如果你只用长度为1的字符串数组,那就没问题了。我想。不过不幸的是,你不能在Python中伪装系统,把它拆分成单个字符的数组,比如 ['a','a','b','b'.. - 如果数组长度不匹配,它会报错。

关于你的第二个问题,如果你用

  character*2

这种写法声明,它实际上可以直接传递一个普通的Python字符串列表:

  testa4.testa4(['aa','bb','cc'])

(如果你尝试使用numpy字符串数组,它会报错。)这些字符串必须是完全正确的长度,否则这里也会报错。

3

根据文档,f2py希望字符串数组以dtype='c'的形式传递(也就是'|S1')。这样做可以解决一部分问题,不过在后台还有一些奇怪的数组形状问题(例如,在我的很多测试中,我发现Fortran会保持2个字符的长度,但把6个字符解释为一个2x6的数组,这样输出时就会得到一些随机的内存数据)。据我所知,这要求你把Fortran数组当作一个二维字符数组来处理,而不是一维的“字符串”数组。不幸的是,我没能让它接受假定的形状,最后只能把字符串的数量作为一个参数传入。

我很确定我遗漏了一些比较明显的东西,但目前这样应该可以工作。至于为什么CHARACTER*2不起作用……我真的不知道。

MODULE char_test

CONTAINS

SUBROUTINE print_strings(strings, n_strs)
    IMPLICIT NONE

    ! Inputs
    INTEGER, INTENT(IN) :: n_strs
    CHARACTER, INTENT(IN), DIMENSION(2,n_strs) :: strings

!f2py INTEGER, INTENT(IN) :: n_strs
!f2py CHARACTER, INTENT(IN), DIMENSION(2,n_strs) :: strings

    ! Misc.
    INTEGER*4 :: j


    DO j=1, n_strs
        WRITE(*,*) strings(:,j)
    END DO

END SUBROUTINE print_strings

END MODULE char_test

----------------

import numpy as np
import char_test as ct

strings = np.array(['aa', 'bb', 'cc'], dtype='c').T
ct.char_test.print_strings(strings, strings.shape[1])

strings = np.array(['ab', 'cd', 'ef'], dtype='c').T
ct.char_test.print_strings(strings, strings.shape[1]) 

-->python run_char_test.py
 aa
 bb
 cc
 ab
 cd
 ef
3

我不知道怎么用 f2py 来做到这一点。不过可以用 ctypes 来实现。你会得到一个字符数组,但你可以很简单地把它转换成字符串。

subroutine testa4(strvar) bind(C, name='testa4')
  use iso_c_binding
  implicit none

  character(len=1,kind=c_char), intent(in) :: strvar(2,3)

  print *, strvar(:,2)

end subroutine testa4

编译命令是: gfortran -shared -fPIC testa4.f90 -o testa4.so

import numpy as np
import ctypes

testa4 = ctypes.CDLL("./testa4.so")

strvar = np.asarray(['aa','bb','cc'], dtype = np.dtype('a2'))
strvar_p = ctypes.c_void_p(strvar.ctypes.data)

testa4.testa4(strvar_p)

运行命令:

> python testpy.f90 
 bb

撰写回答