问题
My aim is to print the 2nd string from a python numpy array in fortran, but I only ever get the first character printed, and it's not necessarily the right string either.
Can anyone tell me what the correct way to pass full string arrays to fortran?
The code is as follows:
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
Compiled with
f2py -c -m testa4 testa4.f90
Output of above code
c
Desired output
bb
回答1:
I don't know how to do that using f2py
. But it can be done with ctypes
. You get an array of characters, but you can convert it to a string very easily.
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
compile: 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)
run:
> python testpy.f90
bb
回答2:
Per the documentation, f2py likes string arrays to be passed with dtype='c' (i.e., '|S1'). This gets you part of the way there, although there are some oddities with array shape going on behind the scenes (e.g., in a lot of my tests I found that fortran would keep the 2 character length, but interpret the 6 characters as being indicative of a 2x6 array, so I'd get random memory back in the output). This (as far as I could tell), requires that you treat the Fortran array as a 2D character array (as opposed to a 1D "string" array). Unfortunately, I couldn't get it to take assumed shape and ended up passing the number of strings in as an argument.
I'm pretty sure I'm missing something fairly obvious, but this should work for the time being. As to why CHARACTER*2 doesn't work ... I honestly have no idea.
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:
Not really an answer but too long to comment: maybe this helps..
in your first case what gets passed in to fortran is for some reason the first character of each string:
'abc'
which in fortran ends up in the length 2 arrays as 'ab' , 'c'. If you work strictly with length one string arrays all is well, I suppose. Unfortunately you cant fake the system and split into a single character array in python ['a','a','b','b'..
- it throws an error if the array lengths don't match.
On your second question, if you declare with the
character*2
notation, it actually works to simply pass a regular python string list:
testa4.testa4(['aa','bb','cc'])
(and now throws an error if you try the numpy string array.). The strings must be the exact correct length or it throws an error here as well.
来源:https://stackoverflow.com/questions/22293180/passing-numpy-string-format-arrays-to-fortran-using-f2py