最近部分功能在用golang build成c库,然后让python ctypes调用。

###实现过程

  1. 根据*C.char, [][]byte生成一块内存空间
  2. 遍历[][]byte,根据index, *C.char的尺寸来做指针运算
  3. 在适当的位置进行赋值
  4. 做类型转换
  5. cyptes中设置restype = ctypes.POINTER(ctypes.c_char_p)
  6. ctypes中再传一 个ctypes.c_int的指针进去, 告诉python有多少个字符串元素
golang
//export PyFindAll
func PyFindAll(re_rule *C.char, content *C.char, total *C.int, content_length C.int) **C.char {
    go_re_rule, go_content := C.GoString(re_rule), C.GoBytes(unsafe.Pointer(content), content_length)
    go_result := FindAll(go_re_rule, go_content)

    var b *C.char
    ptr_size := unsafe.Sizeof(b) // 计算出一个char指针的长度

    ptr := C.malloc(C.size_t(len(go_result)) * C.size_t(ptr_size)) // 申请一块空间, 长度为go_result个数 * char指针的长度

    // defer C.free(unsafe.Pointer(content))

    //  defer C.free(ptr)

    for index := 0; index < len(go_result); index++ {
        element := (**C.char)(unsafe.Pointer(uintptr(ptr) + uintptr(index)*ptr_size)) // 移位操作*char
        *element = C.CString(string(go_result[index]))
    }

    *total = (C.int)(len(go_result))

    return (**C.char)(ptr)
}


python
import time

content = open("index.html", "rb").read()

import ctypes

lib = ctypes.CDLL("re.so")
lib.PyFindAll.restype = ctypes.POINTER(ctypes.c_char_p)
#lib.PyFindAll.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_int)]

total = ctypes.c_int(0)
content_length = ctypes.c_int(len(content))
def test():
    c_result = lib.PyFindAll('<div id="(footer)"[\s|\S]+?Build version\s+?(.+?)[\s|\<]', content, ctypes.pointer(total), content_length)
    print([ c_result[i] for i in range(total.value)])

if __name__ == "__main__":
    begin = time.time()
    for i in range(1000):
        test()

    print(time.time() - begin)