【问题标题】:Returning non-garbage values in char* from C function to python in Swig将 char* 中的非垃圾值从 C 函数返回到 Swig 中的 python
【发布时间】:2020-01-12 06:49:16
【问题描述】:

我正在尝试使用来自 python 脚本的 C 函数。该函数返回一个数组char*。 函数如下:

char* getData(char* id) {
    char* old_place = malloc(sizeof(char) * 256);
    if (modifyArr(id,old_place) == 0) {
        return old_place;
    }
    return NULL;
}

在 Python 中我有:

old = Data.getData(id)

我收到以下错误:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc8 in position 0: invalid continuation byte

那是因为 old_place 没有使用它的所有 256 字符,然后我有垃圾值通过了。如何只返回非垃圾值?

编辑:关于字符串终止符('\0'),在modifyArr我改成old_place这样:

sscanf(line," %s ",old_place);

我读取了文件的行并将所需的行插入old_place,因此我不确定如何添加字符串终止符。

【问题讨论】:

  • 您在使用字符串吗?你还记得添加字符串终止符吗?
  • @Someprogrammerdude 感谢您提到的“字符串终止符”。请看我的编辑。
  • modifyArr 是做什么的?你不是还有内存泄漏吗?
  • @MFisherKDX 在 Data.i 文件中,我使用 %newobject getData,所以我认为我没有。 modifyArr 只是从文件中获取所需的行并插入到该数组中。
  • 如果你的函数返回NULL,你如何释放分配的值old_place

标签: python c swig


【解决方案1】:

两种选择:

  1. 修改 C 函数,使其实际上只返回“有用的”数据,调整数组大小并使其成为有效的 UTF-8 字符串。
  2. 使用ctypes.c_char_p 在 Python 中转换调用结果。

您实际需要什么显然取决于具体情况。


如果您使用 ASCII 值,方法 1 可以这样完成:

char* getData(char* id) {
    char* old_place = malloc(sizeof(char) * 256);
    size_t i;

    if (modifyArr(id, old_place) == 0) {
        for (i = 0; i < 256-1; i++) {
            if (old_place[i] > 127)
                break; // Stop at the first non-ASCII value.
        }

        old_place[i] = '\0'; // Correctly terminate the string.

        return old_place;
    }

    return NULL;
}

这将生成一个有效的字符串,您将能够在 Python 中使用它。


方法二可以这样:

import ctypes

mylib = ctypes.cdll.LoadLibrary('./mylib.so')

data_id = "something"
data = (ctypes.c_char_p)(mylib.getData(data_id))

# data.value is now a bytes() object
print(data.value)

输出:

b'abcdwhatever\xfd\x8a\xc5\x7f...'

【讨论】:

  • 你能解释一下第一个解决方案吗?
  • @vesii 也添加了方法 1。
【解决方案2】:

尝试将bzero 应用于old_place

char* getData(char* id) {
    char* old_place = malloc(sizeof(char) * 256);
    bzero(old_place, 256);
    if (modifyArr(id,old_place) == 0) {
        return old_place;
    }
    return NULL;
}

bzero 设置为 \0 n 您传入的 char* 字节。 所以这样你就可以删除char* 中的所有垃圾值,你malloc

请参阅有关此功能的参考: http://man7.org/linux/man-pages/man3/bzero.3.html

编辑

根据 cmets 更新答案,感谢MFisherKDX

另外更正确的做法是将memset 应用于old_place

char* getData(char* id) {
    char* old_place = malloc(sizeof(char) * 256);
    memset(old_place, 0, 256);
    if (modifyArr(id,old_place) == 0) {
        return old_place;
    }
    return NULL;
}

memset 设置char* 的第一个n 字节,您将其与char 一起传递给它,该数字在ASCII 表中作为第二个参数传递。 在您的情况下,使用 0(即\0)作为char 写入char*

参考memset:https://www.tutorialspoint.com/c_standard_library/c_function_memset.htm

编辑 2:好的,很公平,calloc 比上述操作更简单。 只需在您的函数中替换 malloc 上的 calloc

char* getData(char* id) {
    char* old_place = calloc(256, sizeof(char));
    if (modifyArr(id,old_place) == 0) {
        return old_place;
    }
    return NULL;
}

callocmalloc 执行相同的操作,并将\0 写入所有分配的字节。

【讨论】:

  • bzero?那是什么来的?
  • @MarcoBonelli 我添加了关于它的参考
  • @MFisherKDX 我已经在写一个关于memset 的更新作为替代和更通用的方法=) 但无论如何,我不知道bzero 已被弃用。
  • 顺便说一句,@DmitriyFialkovskiy 为什么在有calloc() 时使用malloc + memset(..., 0) 可以做到这一点?更简单,不是吗?
  • calloc 有两个相乘的参数。所以使用calloc(256, 1)calloc(256, sizeof(char))calloc(256, sizeof(*old_place))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-19
  • 1970-01-01
  • 2014-07-20
  • 1970-01-01
  • 2013-10-30
  • 1970-01-01
相关资源
最近更新 更多