为了账号安全,请及时绑定邮箱和手机立即绑定

用于fortran库的python中结构内部和外部的c_char数组之间的区别

用于fortran库的python中结构内部和外部的c_char数组之间的区别

慕仙森 2022-07-19 20:11:12
我正在使用 c_types 将 fortran 库与 python 连接。我在python中初始化结构,将它们传递给填充它们的fortran,然后在python中读回它们。数字数组一切正常,但现在我被接口字符串数组困住了。我试过这样的例子,这没问题,但在这种情况下,c_char 数组不在结构中。因此,我尝试修改前面的示例,将 c_char 数组放入结构中。这是我使用的代码,有和没有结构:Python代码:    from ctypes import *    lib = CDLL("./libf.so")    if 1:        print(">>> Without structure")        func = getattr(lib, "fortran2py_")        nstring = pointer(c_long(2))        carr = (c_char * 255)()        func.argtypes = [POINTER(c_long), POINTER(c_char)]        print(type(carr))        print('before:',carr)        func(nstring, carr)        str1, str2 = ''.join([v.decode("utf-8") for v in carr]).rstrip("\x00").split("\x00")        print(str1, str2)    class Struct0(Structure):        _fields_ = [            ("name", c_char * 255),        ]    if 1:            print(">>> With structure")        func = getattr(lib, "fortran2pystr_")        nstring = pointer(c_long(2))        carr = Struct0()        func.argtypes = [POINTER(c_long), POINTER(Struct0)]        print(type(carr.name))        print('before:',carr.name)        func(nstring, byref(carr))        print('after:',carr.name)Fortran 代码:    module c_interop        use iso_c_binding        implicit none        integer, parameter :: STRLEN = 64        type, bind(c) :: charStr           character(c_char)  :: name(255)        end type charStr我没有得到任何错误,除了在修改部分,Fortran 应该填充 c_char carr.name 在 mystr 的元素上循环的数组,但结果字符串只包含第一个元素。当carr不是结构体而是直接c_char数组时,python可以读取mystr的所有内容。如您所见, carr 和 carr.name 的类型也不相同。您知道我修改后的代码有什么问题吗?谢谢 !如所见,数据字段类型已更改我想到的最简单的解决方案是将字符串分隔符从NUL替换为您确定它不会出现在任何字符串中的另一个字符。我选择了0xFF ( 255 )。我认为包含 的结构也可以,但它会更复杂一些(另外,我没有测试它)ctypes.POINTER(ctypes.c_char)我的Fortran知识非常接近0,但是fortran2pystr看起来不太对劲。我不知道Fortran类型是如何构造的,但是从Python传递一个包装在结构指针中的char数组(实际上,它们具有相同的地址)并像普通char数组一样处理它似乎是错误的。更改struct可能会导致灾难
查看完整描述

1 回答

?
扬帆大鱼

TA贡献1799条经验 获得超9个赞

原因是CTypes的微妙行为。当 c_char(以及c_wchar )数组作为结构中的字段存在时,它们会被静默转换为字节(或str )。这是通过以NUL终止的c_char_p(或c_wchar_p)完成的,这意味着如果遇到NUL(0x00 )字符,“数组”将被截断,这正是您的情况。您可以通过查看字段类型来检查。不知道为什么会这样(也许是为了方便使用),但在某些情况下它弊大于利。它可以用

仅限Python代码。


code00.py


#!/usr/bin/env python


import sys

import ctypes as ct



ARR_DIM = 10

CharArr = ct.c_char * ARR_DIM



class CharArrStruct(ct.Structure):

    _fields_ = [

        ("data", CharArr),

    ]



def print_array(arr,  text, size=ARR_DIM):

    print(text)

    for i in range(size):

        print("{0:3d}".format(i), end=" - ")

        try:

            print(arr[i])

        except IndexError:

            print("IndexError!!!")

            break

    print()



def main(*argv):

    arr = CharArr()

    sarr = CharArrStruct()

    print("Array (plain) type: {0:}".format(type(arr)))

    print("Array (in structure) type: {0:}".format(type(sarr.data)))


    string_separator = b"\x00"

    print("\nString separator: {0:}".format(string_separator))

    text = string_separator.join((b"abcd", b"efgh"))

    arr[0:len(text)] = text

    sarr.data = text


    print_array(arr, "Plain array:")

    print_array(sarr.data, "Structure with array:")

    print("Strings (in structure): {0:}".format(sarr.data.split(string_separator)))


    string_separator = b"\xFF"

    print("\nString separator: {0:}".format(string_separator))

    sarr.data = string_separator.join((b"abcd", b"efgh"))


    print_array(sarr.data, "Structure with array:")

    print("Strings (in structure): {0:}".format(sarr.data.split(string_separator)))



if __name__ == "__main__":

    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))

    main(*sys.argv[1:])

    print("\nDone.")

输出:


e:\Work\Dev\StackOverflow\q060093054>"e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py

Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32


Array (plain) type: <class '__main__.c_char_Array_10'>

Array (in structure) type: <class 'bytes'>


String separator: b'\x00'

Plain array:

  0 - b'a'

  1 - b'b'

  2 - b'c'

  3 - b'd'

  4 - b'\x00'

  5 - b'e'

  6 - b'f'

  7 - b'g'

  8 - b'h'

  9 - b'\x00'


Structure with array:

  0 - 97

  1 - 98

  2 - 99

  3 - 100

  4 - IndexError!!!


Strings (in structure): [b'abcd']


String separator: b'\xff'

Structure with array:

  0 - 97

  1 - 98

  2 - 99

  3 - 100

  4 - 255

  5 - 101

  6 - 102

  7 - 103

  8 - 104

  9 - IndexError!!!


Strings (in structure): [b'abcd', b'efgh']


Done.

备注:

  • 如所见,数据字段类型已更改

  • 我想到的最简单的解决方案是将字符串分隔符从NUL替换为您确定它不会出现在任何字符串中的另一个字符。我选择了0xFF ( 255 )。我认为包含 的结构也可以,但它会更复杂一些(另外,我没有测试它)ctypes.POINTER(ctypes.c_char)

  • 我的Fortran知识非常接近0,但是fortran2pystr看起来不太对劲。我不知道Fortran类型是如何构造的,但是从Python传递一个包装在结构指针中的char数组(实际上,它们具有相同的地址)并像普通char数组一样处理它似乎是错误的。更改struct可能会导致灾难


查看完整回答
反对 回复 2022-07-19
  • 1 回答
  • 0 关注
  • 71 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信