python調用pybind11導出的pyd,出現UnicodeDecodeError

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. 對稱…