解决C++调用Python中文乱码

部署运行你感兴趣的模型镜像

一、问题描述

在 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处理完成

五、注意事项

  1. Python文件编码​​: 必须使用UTF-8编码保存Python脚本。
  2. Python版本​​: 本文方案适用于Python 3。
  3. Qt依赖​​: 如果使用Qt方案,需要链接QtCore模块。
  4. 内存管理​​: 注意及时释放分配的内存资源。

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值