一、问题描述
在 C++ 与 Python 混合编程时,很多教程都介绍了参数传递和返回值处理的方法。但是当需要传递中文字符作为参数时,经常会遇到编码问题,导致乱码甚至程序崩溃。经过研究和实践,我找到了解决中文乱码的有效方法,在这里分享给大家。
二、问题根源分析
2.1 编码差异导致的问题
通常我们使用 PyObject* objArg = Py_BuildValue("s", "Hello") 来构建参数,但当输入参数为中文时,objArg 会返回空指针。这是因为:
- Python 默认使用 UTF-8 编码;
- windows 平台 C++ 中的中文大多采用 GBK 编码;
- 中文采用两个字节表示一个汉字,而
Py_BuildValue构建参数时还是按单字节处理构建的,导致构建失败。
2.2 正确的构建方法
对于中文参数,应该使用:
// Python 3.3 以前
PyObject* PyUnicode_FromUnicode(const Py_UNICODE *u, Py_ssize_t size);
// Python 3.3 引入了新的Unicode API,旧方法PyUnicode_FromUnicode被弃用
PyObject* PyUnicode_FromWideChar(const wchar_t *wstr, Py_ssize_t size);
此函数接受 Unicode 字符串和字符个数(中文两个字节算一个字符)。
三、解决方案
方法1:Windows平台专用方案
如果你在 Windows 下开发,可以使用 Windows API 进行编码转换,实现 GBK 编码的字符转换为 Unicode 字符串:
#include <windows.h>
PyObject* StringToPyByWin(const std::string& str) {
// 计算需要的宽字符缓冲区大小
int wlen = ::MultiByteToWideChar(CP_ACP, NULL, str.c_str(), int(str.size()), NULL, 0);
// 分配宽字符缓冲区
wchar_t* wszString = new wchar_t[wlen + 1];
// 执行GBK到Unicode的转换
::MultiByteToWideChar(CP_ACP, NULL, str.c_str(), int(str.size()), wszString, wlen);
wszString[wlen] = '\0';
// 创建Python Unicode对象
PyObject* pobj = PyUnicode_FromUnicode(reinterpret_cast<const Py_UNICODE*>(wszString), wlen);
// 清理资源
delete[] wszString;
return pobj;
}
该函数直接将 std::string 字符串转换为 PyObject*,可直接使用。
方法2:跨平台Qt方案
如果需要跨平台兼容,可采用Qt的QTextCodec实现string转换为QString(QString采用Unicode编码),具体实现代码如下:
编码转换工具类 (GBK.hpp)
#pragma once
#include <QString>
#include <QTextCodec>
#include <string>
class GBK {
public:
// std::string (GBK编码) 转 QString(Unicode编码)
static QString ToUnicode(const std::string& cstr) {
QTextCodec* pCodec = QTextCodec::codecForName("gb2312");
if (!pCodec)
return {};
return pCodec->toUnicode(cstr.c_str(), cstr.length());
}
// QString(Unicode编码) 转 std::string (GBK编码)
static std::string FromUnicode(const QString& qstr) {
QTextCodec* pCodec = QTextCodec::codecForName("gb2312");
if (!pCodec)
return {};
QByteArray arr = pCodec->fromUnicode(qstr);
return std::string(arr.data(), arr.size());
}
};
使用Qt进行字符串转换
PyObject* StringToPyByQt(const std::string& str) {
// 转换为Unicode编码
const QString unicodeStr = GBK::ToUnicode(str);
// 转换为宽字符数组
int sLen = unicodeStr.size();
wchar_t* wChStr = new wchar_t[sLen + 1];
unicodeStr.toWCharArray(wChStr);
wChStr[sLen] = '\0';
// 创建Python Unicode对象
PyObject* pobj = PyUnicode_FromUnicode(reinterpret_cast<const Py_UNICODE*>(wChStr), sLen);
// 清理资源
delete[] wChStr;
return pobj;
}
四、完整示例代码
具体如何实现C++调用Python并实现中文参数的传递呢?下面提供完整的可运行示例,使用Qt方案进行编码转换:
Python脚本 (testCallPython.py)
# 必须以UTF-8编码保存
def callTest(string):
print('接收到的字符串:' + string)
#返回结果
reStr = 'Python处理完成'
return reStr
C++主程序 (main.cpp)
#include "GBK.hpp"
#include <Python.h>
#include <iostream>
#include <windows.h>
// Windows方案转换函数
PyObject* StringToPyByWin(const std::string& str) {
const int wlen = ::MultiByteToWideChar(CP_ACP, NULL, str.c_str(), int(str.size()), NULL, 0);
wchar_t* wszString = new wchar_t[wlen + 1];
::MultiByteToWideChar(CP_ACP, NULL, str.c_str(), int(str.size()), wszString, wlen);
wszString[wlen] = '\0';
PyObject* pobj = PyUnicode_FromUnicode(reinterpret_cast<const Py_UNICODE*>(wszString), wlen);
delete[] wszString;
return pobj;
}
// Qt方案转换函数
PyObject* StringToPyByQt(const std::string& str) {
const int sLen = GBK::ToUnicode(str).size();
wchar_t* wChStr = new wchar_t[sLen + 1];
GBK::ToUnicode(str).toWCharArray(wChStr);
wChStr[sLen] = '\0';
PyObject* pobj = PyUnicode_FromUnicode((const Py_UNICODE*)wChStr, sLen);
delete[] wChStr;
return pobj;
}
// 调用python脚本函数
bool callTest(const std::string& str) {
// 初始化Python解释器
Py_Initialize();
if (!Py_IsInitialized()) {
std::cerr << "初始化Python失败!\n";
return false;
}
// 导入sys模块
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('D:/CallPython')"); // 存放脚本python脚本的目录(testCallPython.py文件)
// 导入testCallPython.py脚本模块
PyObject* pModule = PyImport_ImportModule("testCallPython");
if (!pModule) {
std::cerr << "无法加载testCallPython模块!\n";
return false;
}
// 获取函数
PyObject* pFunc = PyObject_GetAttrString(pModule, "callTest");
if (!pFunc) {
std::cerr << "找不到目标函数!\n";
return false;
}
// 处理中文字符串
PyObject* pKeyWord = StringToPyByQt(str);
// 以元组的形式打包参数
PyObject* pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, pKeyWord);
// 调用Python函数
PyObject* pReturn = PyEval_CallObject(pFunc, pArgs);
// 处理返回值
char* result = new char[32] {};
PyArg_Parse(pReturn, "s", &result);
const std::string reStr = GBK::FromUnicode(result);
std::cout << "C++调用Python返回结果为:" << reStr << std::endl;
//结束,释放python解释器
Py_Finalize();
delete[] result;
return true;
}
int main(int argc, char* argv[]) {
callTest("This is 中文测试");
std::cin.get();
return 0;
}
运行结果
接收到的字符串:This is 中文测试
C++调用Python返回结果为: Python处理完成
五、注意事项
- Python文件编码: 必须使用UTF-8编码保存Python脚本。
- Python版本: 本文方案适用于Python 3。
- Qt依赖: 如果使用Qt方案,需要链接QtCore模块。
- 内存管理: 注意及时释放分配的内存资源。

3818





