python调用pybind11导出的pyd,出现UnicodeDecodeError

1. 问题描述

  • 举个例子,当有以下C++代码以及Pybind11的绑定代码时,在python访问包含中文的Name和Value会有UnicodeDecodeError的异常!

    	class VxUserProp{public:VxUserProp();VxUserProp(const string &strName,const string &strVal);string m_strName;string m_strVal;};py::class_<VxUserProp>(m, "VxUserProp").def(py::init<>()).def(py::init<const std::string&, const std::string&>()).def_readwrite("Name", &VxUserProp::m_strName, "属性名称").def_readwrite("Value", &VxUserProp::m_strVal, "属性值").def("__repr__", [](const VxUserProp& prop) {return "VxUserProp(Name='" + prop.m_strName + "', Value='" + prop.m_strVal + "')";});
    

2. 原因分析

  • 经过查看官方文档和分析,原因是我的C++代码是gbk编码,string中存的是gbk编码的数据。当 C++函数返回 std::stringchar* 给 Python 调用者时,pybind11 会假定该字符串是有效的 UTF-8,并将其解码为原生 Python str ,使用与 Python 执行 bytes.decode('utf-8') 相同的 API。如果这种隐式转换失败,pybind11 将引发 UnicodeDecodeError

3. 解决方案

3.1 方案一:显示转换

  • 根据官方文档字符串、字节和 Unicode 转换 - pybind11 文档 — Strings, bytes and Unicode conversions - pybind11 documentation

  • 如果某些 C++代码构造的 std::string 不是 UTF-8 字符串,可以进行显式转换并返回一个 py::str 对象。显式转换与隐式转换具有相同的开销。

    // This uses the Python C API to convert Latin-1 to Unicode
    m.def("str_output",[]() {std::string s = "Send your r\xe9sum\xe9 to Alice in HR"; // Latin-1py::handle py_s = PyUnicode_DecodeLatin1(s.data(), s.length(), nullptr);if (!py_s) {throw py::error_already_set();}return py::reinterpret_steal<py::str>(py_s);}
    );
    
  • 显而易见的是,这种写法,工作量太大!

3.2 方案二:修改pybind11源码

  • 根据官方文档概述 - pybind11 文档 — Overview - pybind11 documentation中所介绍的,它内置了一些类型转换。我们只需要找到这个转换的代码进行修改即可

  • 最终我们在include/pybind11/cast.h中找到了string_caster,并进行了修改!具体修改参考以下代码中的"load修改部分"和"cast修改部分"。进行以下修改后,其他地方不用进行任何修改! 对比上一种方法,工作量要小得多。

    // Helper class for UTF-{8,16,32} C++ stl strings:
    template <typename StringType, bool IsView = false>
    struct string_caster {using CharT = typename StringType::value_type;// Simplify life by being able to assume standard char sizes (the standard only guarantees// minimums, but Python requires exact sizes)static_assert(!std::is_same<CharT, char>::value || sizeof(CharT) == 1,"Unsupported char size != 1");
    #if defined(PYBIND11_HAS_U8STRING)static_assert(!std::is_same<CharT, char8_t>::value || sizeof(CharT) == 1,"Unsupported char8_t size != 1");
    #endifstatic_assert(!std::is_same<CharT, char16_t>::value || sizeof(CharT) == 2,"Unsupported char16_t size != 2");static_assert(!std::is_same<CharT, char32_t>::value || sizeof(CharT) == 4,"Unsupported char32_t size != 4");// wchar_t can be either 16 bits (Windows) or 32 (everywhere else)static_assert(!std::is_same<CharT, wchar_t>::value || sizeof(CharT) == 2 || sizeof(CharT) == 4,"Unsupported wchar_t size != 2/4");static constexpr size_t UTF_N = 8 * sizeof(CharT);bool load(handle src, bool) {
    #ifdef _WIN32  //----------------------load修改部分-----------------------------if constexpr (std::is_same<StringType, std::string>::value) {if (!src) return false;if (!PyUnicode_Check(src.ptr())) return false;// 总是用GBK编码PyObject* gbk_bytes = PyUnicode_AsEncodedString(src.ptr(), "gbk", "replace");if (!gbk_bytes) return false;const char* gbk_data = PyBytes_AS_STRING(gbk_bytes);Py_ssize_t gbk_size = PyBytes_GET_SIZE(gbk_bytes);value.assign(gbk_data, gbk_size);Py_DECREF(gbk_bytes);return true;}
    #endif		//-----------------------load修改部分结束----------------------------------// 其它类型/平台走原逻辑handle load_src = src;if (!src) {return false;}if (!PyUnicode_Check(load_src.ptr())) {return load_raw(load_src);}// For UTF-8 we avoid the need for a temporary `bytes` object by using// `PyUnicode_AsUTF8AndSize`.if (UTF_N == 8) {Py_ssize_t size = -1;const auto *buffer= reinterpret_cast<const CharT *>(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size));if (!buffer) {PyErr_Clear();return false;}value = StringType(buffer, static_cast<size_t>(size));return true;}auto utfNbytes= reinterpret_steal<object>(PyUnicode_AsEncodedString(load_src.ptr(),UTF_N == 8    ? "utf-8": UTF_N == 16 ? "utf-16": "utf-32",nullptr));if (!utfNbytes) {PyErr_Clear();return false;}const auto *buffer= reinterpret_cast<const CharT *>(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr()));size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT);// Skip BOM for UTF-16/32if (UTF_N > 8) {buffer++;length--;}value = StringType(buffer, length);// If we're loading a string_view we need to keep the encoded Python object alive:if (IsView) {loader_life_support::add_patient(utfNbytes);}return true;}static handlecast(const StringType &src, return_value_policy /* policy */, handle /* parent */) {
    #ifdef _WIN32   //----------------------cast修改部分-----------------------------if constexpr (std::is_same<StringType, std::string>::value) {// 直接用GBK解码PyObject* pystr = PyUnicode_Decode(src.data(), (Py_ssize_t)src.size(), "gbk", "replace");if (!pystr) return pybind11::none().release();return pybind11::handle(pystr);}
    #endif			//----------------------cast修改部分-----------------------------// 其它类型/平台走原逻辑const char *buffer = reinterpret_cast<const char *>(src.data());auto nbytes = ssize_t(src.size() * sizeof(CharT));handle s = decode_utfN(buffer, nbytes);if (!s) {throw error_already_set();}return s;}PYBIND11_TYPE_CASTER(StringType, const_name(PYBIND11_STRING_NAME));private:static handle decode_utfN(const char *buffer, ssize_t nbytes) {
    #if !defined(PYPY_VERSION)return UTF_N == 8    ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr): UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr): PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr);
    #else// PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as// well), so bypass the whole thing by just passing the encoding as a string value, which// works properly:return PyUnicode_Decode(buffer,nbytes,UTF_N == 8    ? "utf-8": UTF_N == 16 ? "utf-16": "utf-32",nullptr);
    #endif}// When loading into a std::string or char*, accept a bytes/bytearray object as-is (i.e.// without any encoding/decoding attempt).  For other C++ char sizes this is a no-op.// which supports loading a unicode from a str, doesn't take this path.template <typename C = CharT>bool load_raw(enable_if_t<std::is_same<C, char>::value, handle> src) {if (PYBIND11_BYTES_CHECK(src.ptr())) {// We were passed raw bytes; accept it into a std::string or char*// without any encoding attempt.const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr());if (!bytes) {pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure.");}value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr()));return true;}if (PyByteArray_Check(src.ptr())) {// We were passed a bytearray; accept it into a std::string or char*// without any encoding attempt.const char *bytearray = PyByteArray_AsString(src.ptr());if (!bytearray) {pybind11_fail("Unexpected PyByteArray_AsString() failure.");}value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr()));return true;}return false;}template <typename C = CharT>bool load_raw(enable_if_t<!std::is_same<C, char>::value, handle>) {return false;}
    };

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87561.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87561.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/87561.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL别名在GROUP BY中的使用规则

-- 设置变量&#xff1a;SET earliest_date ... 用于定义并赋值一个用户变量 earliest_date。 -- 用户定义的变量必须以 符号开头&#xff0c;例如 earliest_date。 -- 符号是MySQL中用户变量的标识符&#xff0c;用于区分系统变量和用户变量。 SET earliest_date (SELECT …

2025.7.4总结

感恩环节:感谢今日工作顺利度过&#xff0c;明天终于能美美的睡个懒觉了。感谢这周有个美好的双休。今日去实验室参观设备&#xff0c;感谢我的一个同事解答了我关于硬件设备与所做软件业务之间的关系&#xff0c;通过控制器控制网元等相关设备&#xff0c;同时&#xff0c;虽然…

Prompt 精通之路(五)- 构建你的“AI 指令系统”:超越简单提问的 CRISPE 与 APE 框架

&#x1f680; Prompt 精通之路&#xff1a;系列文章导航 第一篇&#xff1a;[本文] AI 时代的新语言&#xff1a;到底什么是 Prompt&#xff1f;为什么它如此重要&#xff1f;第二篇&#xff1a;告别废话&#xff01;掌握这 4 个黄金法则&#xff0c;让你的 Prompt 精准有效第…

#NFT艺术品哈希值唯一性与《民法典》“网络虚拟财产”认定的冲突

首席数据官高鹏律师数字经济团队创作&#xff0c;AI辅助 一、当区块链的「绝对唯一」遇上法律的「弹性空间」 每个NFT艺术品背后的哈希值&#xff0c;都像用数学密码刻在区块链上的指纹——世界上没有任何两个完全相同的编码。这种由0和1构筑的「数字DNA」&#xff0c;被技术信…

【arXiv2025】计算机视觉|即插即用|LBMamba:革新视觉模型效率,性能炸裂

论文地址&#xff1a;https://arxiv.org/pdf/2506.15976 代码地址&#xff1a;https://github.com/CiaoHe/bi-mamba 关注UP CV缝合怪&#xff0c;分享最计算机视觉新即插即用模块&#xff0c;并提供配套的论文资料与代码。 https://space.bilibili.com/473764881 摘要 Mamba…

【狂飙AGI】第7课:AGI-行业大模型(系列1)

目录 &#xff08;一&#xff09;服装史的GPT时刻&#xff08;二&#xff09;AI多学科诊疗系统&#xff08;三&#xff09;医疗大模型&#xff08;四&#xff09;生物医药大模型&#xff08;五&#xff09;教育大模型&#xff08;六&#xff09;心理大模型&#xff08;七&#…

(LeetCode 每日一题) 3307. 找出第 K 个字符 II (位运算、数学)

题目&#xff1a;3307. 找出第 K 个字符 II 思路&#xff1a;位运算&#xff0c;时间复杂度0(logk)。 当2^(i-1) <k 且 2^i>k &#xff0c;说明k在K2^i的右半段 &#xff0c;k和其前半段的某个字符有关系 即当k>K时&#xff0c;k是由k-K位置上的字符变化而来&#xf…

国产MCU学习Day4——CW32F030C8T6:独立看门狗功能全解析

CW32F030C8T6 看门狗功能概述 CW32F030C8T6 是芯源半导体&#xff08;WCH&#xff09;推出的 Cortex-M0 内核微控制器&#xff0c;内置独立看门狗&#xff08;IWDG&#xff09;和窗口看门狗&#xff08;WWDG&#xff09;&#xff0c;用于检测和恢复系统异常状态。 一.独立看门…

SAP升级过程中如何确保数据安全?

目录 升级过程中可能遇到的数据风险 升级前的准备工作 升级过程中的保护措施 升级后的验证工作 在数字化转型浪潮中&#xff0c;SAP系统作为企业核心业务运营的系统&#xff0c;其升级过程不仅关乎技术架构的革新&#xff0c;更直接关系到企业最宝贵的资产——数据安全。一…

Vue 3 + Element Plus 常见开发问题与解决方案手册

&#x1f31f;Vue 3 Element Plus 常见开发问题与解决方案手册 &#x1f9e0; 本文整理了常见但容易混淆的几个 Vue 3 前端开发问题&#xff0c;包括插槽、原型链、响应式数据处理、v-model 报错、样式阴影控制等&#xff0c;建议收藏学习&#xff01; &#x1f4cc;一、动态插…

Spring Boot + 本地部署大模型实现:安全性与可靠性保障

在将大语言模型集成到 Spring Boot 应用中时&#xff0c;安全性和可靠性是两个关键因素。本地部署的大模型虽然提供了强大的功能&#xff0c;但也可能带来一些安全风险&#xff0c;如数据泄露、模型被恶意利用等。本文将介绍如何在 Spring Boot 应用中保障本地部署大模型的安全…

Zookeeper 客户端 .net访问框架 ZookeeperNetEx项目开发编译

一、项目简介 ZooKeeperNetEx 项目是一个针对.NET开发的异步客户端库&#xff0c;旨在为开发者提供高效且可靠的分布式协调服务。‌ 该项目完全基于任务异步编程&#xff0c;兼容.NET 4.61及以上版本&#xff0c;包括.NET Core。ZooKeeperNetEx严格遵循官方Java客户端的逻辑&am…

【学习笔记】因果推理导论第2课

因果推理导论第2课 因果推断假设 前言一、假设1、 Ignorability / Exchangeability2、条件可交换 二、估计 前言 第一节课通过一些例子说明了为什么要做因果推断,以及通过控制混杂因素计算因果效应;这一节课将围绕为何控制混杂因素计算因果效应这一方法成立,讲述其涉及到的一些…

VASP 教程:VASP 机器学习力场微调

机器学习力场&#xff08;Machine-Learned Force Fields, MLFFs&#xff09;作为一种新兴的计算方法&#xff0c;已在第一性原理分子动力学&#xff08;Ab Initio Molecular Dynamics, AIMD&#xff09;模拟中展现出独特优势&#xff08;参见 VASP Wiki&#xff1a;Category:Ma…

Java+Vue开发的仓库管理系统,实时监控库存,精准统筹货物出入与调配

前言&#xff1a; 在当今竞争激烈的商业环境中&#xff0c;高效的仓库管理对于企业的运营和成本控制至关重要。一个完善的仓库管理系统能够帮助企业实现货物的精准存储、快速出入库、实时库存监控以及全面的数据分析&#xff0c;从而提升整体运营效率、降低库存成本、增强客户…

【王阳明代数】热门问答,什么是张量?

【王阳明代数】热门问答&#xff0c;什么是张量&#xff1f; 流形学习基础概念前情提要&#xff0c;张量概念的提出&#xff0c;王船山流形与信息容量的概念回答&#xff1a;什么是张量前&#xff0c;对王船山流形&#xff0c;意气实体的定义再表述&#xff1b;王船山流形分析1…

差分压缩算法(增量更新)

差分压缩算法是一种数据压缩技术&#xff0c;它的核心思想是通过找出数据之间的差异来减少需要存储或传输的数据量。下面从基本原理、常见应用场景、算法示例等方面详细介绍差分压缩算法。 基本原理 差分压缩算法的基本原理是比较相邻数据元素之间的差异&#xff0c;并只记录…

Html5支持的视频文件格式和音频文件格式有哪些?

视频文件格式 MP4&#xff1a;MPEG-4 Part 14&#xff0c;支持H.264编码。几乎所有的浏览器都支持该格式。 WebM&#xff1a;谷歌开发的格式&#xff0c;使用VP8或VP9编码&#xff0c;可以在大多数现代浏览器中播放 Ogg&#xff1a;开放媒体格式&#xff0c;使用Vorbis编码&…

J20250704 算法题5道

题目一览&#xff1a; 606. 根据二叉树创建字符串 - 力扣&#xff08;LeetCode&#xff09; 506. 相对名次 - 力扣&#xff08;LeetCode&#xff09; 1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 100. 相同的树 - 力扣&#xff08;LeetCode&#xff09; 101. 对称…