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

C++调用Python,包括参数传递及返回值的处理的教程都很多。但是对于中文字符作为参数传递时,会遇到编码问题而乱码甚至崩溃。经过搜集资料,终于解决乱码问题,特记录在此:

1、参数传递一般采用PyObject* objArg = Py_BuildValue("s", "Hello")构建参数,当输入参数为中文时,objArg则返回为空指针。原因是:Python为UTF-8编码,而C++中中文大多采用GBK编码。对于中文采用两个字节表示一个汉字,而此函数最终构建参数时还是按一个字节一个字节构建的,所以中文会构建失败。

2、构建中文参数可以采用该函数(PyObject*) PyUnicode_FromUnicode(const Py_UNICODE *u, Py_ssize_t size);此函数的参数为Unicode字符串和字符个数(中文两个字节算一个字符)。

3、GBK编码的字符如何转换为Unicode字符串:

3.1 若为windows下程序,可采用windows的宽字符转换API,具体实现为:

#include <windows.h>

PyObject* StringToPyByWin(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];
	::MultiByteToWideChar(CP_ACP, NULL, str.c_str(), int(str.size()), wszString, wlen);
	wszString[wlen] = '\0';
	PyObject* pobj = PyUnicode_FromUnicode((const Py_UNICODE*)wszString, wlen);
	delete wszString;
	return pobj;
}

该函数直接将string字符串转换为PyObject*,可直接使用;

3.2 若需要在其他平台使用,可采用Qt的QTextCodec实现string转换为QString(QString采用Unicode编码),具体实现代码如下:

#include <QString>
#include <QTextCodec>
#include <string>

// std::string (GBK) -> QString(Unicode)
QString ToUnicode(const string& cstr)
{
        QTextCodec* pCodec = QTextCodec::codecForName("gb2312");
        if(!pCodec)
	    return "";
        QString qstr = pCodec->toUnicode(cstr.c_str(), cstr.length());
        return qstr;
}
另,Qt实现QString转为string的代码如下:
// QString(Unicode) -> std::string (GBK)
string FromUnicode(const QString& qstr)
{
	QTextCodec* pCodec = QTextCodec::codecForName("gb2312");
	if(!pCodec)
		return "";	
	QByteArray arr = pCodec->fromUnicode(qstr);
	string cstr = arr.data();
	return cstr;
}

4、具体如何实现C++调用Python并实现中文参数的传递呢?综合上面的代码,下面附上完整源码,我采用Qt的方式转换中文编码。共3个文件,testCallPython.py、main.cpp和GBK.h:

testCallPython.py(Python文件)

#以UTF-8编码
def callTest(string):
    print('传入的字符串为:' + string)
    #返回结果
    reStr = 'Python返回值'
    return reStr
main.cpp(调用Python的C++文件)
#include <Python.h>
#include <iostream>
#include "GBK.h"
#include <windows.h>
using namespace std;

PyObject* StringToPyByWin(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];
	::MultiByteToWideChar(CP_ACP, NULL, str.c_str(), int(str.size()), wszString, wlen);
	wszString[wlen] = '\0';
	PyObject* pobj = PyUnicode_FromUnicode((const Py_UNICODE*)wszString, wlen);
	delete wszString;
	return pobj;
}

PyObject* StringToPyByQt(std::string str)
{
	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;
	wChStr = NULL;
	return pobj;
}

//测试调取python脚本
bool callTest(string str)
{
	//初始化python模块
	Py_Initialize();
	if (!Py_IsInitialized())
	{
		printf("初始化Python失败!\n");
		return false;
	}
	//导入sys模块
	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('E:\\CallPython\\CallPython')");//存放脚本python脚本的目录(testCallPython.py文件)
	//导入testCallPython.py脚本模块
	PyObject* pModule = PyImport_ImportModule("testCallPython");
	if (!pModule)
	{
		printf("读取Python脚本testCallPython失败!\n");
		return false;
	}
	//获取函数
	PyObject* pFunc = PyObject_GetAttrString(pModule, "callTest");
	if (!pFunc)
	{
		printf("获取函数callTest失败!\n");
		return false;
	}
	//处理中文字符串
	PyObject* pKeyWord = StringToPyByQt(str);
	//以元组的形式打包参数
	PyObject* pArgs = PyTuple_New(1);
	PyTuple_SetItem(pArgs, 0, pKeyWord);
	PyObject* pReturn = PyEval_CallObject(pFunc, pArgs);
	//处理返回值
	char* result = new char[20];
	PyArg_Parse(pReturn, "s", &result);
	string reStr = GBK::FromUnicode(result);
	cout << "C++调用Python返回结果为:" << reStr << endl;
	//结束,释放python解释器
	Py_Finalize();
	return true;
}


int main(int argc, char *argv[])
{
	callTest("This is 中文测试");
	cin.get();
	return 0;
}

GBK.h(Qt下中文转码)

#ifndef _QT_GBK_H
#define _QT_GBK_H

#include <QString>
#include <QTextCodec>
#include <string>
using std::string;

class GBK
{
public:
	// QString(Unicode) -> std::string (GBK)
	static string FromUnicode(const QString& qstr)
	{
		QTextCodec* pCodec = QTextCodec::codecForName("gb2312");
		if(!pCodec)
			return "";	
		QByteArray arr = pCodec->fromUnicode(qstr);
		string cstr = arr.data();
		return cstr;
	}

	// std::string (GBK) -> QString(Unicode)
	static QString ToUnicode(const string& cstr)
	{
		QTextCodec* pCodec = QTextCodec::codecForName("gb2312");
		if(!pCodec)
			return "";
		QString qstr = pCodec->toUnicode(cstr.c_str(), cstr.length());
		return qstr;
	}
};

#endif

5、运行4中的程序,结果为:

注:采用Python3,python文件一定要采用UTF-8编码,否则会读取脚本失败;要使用GBK.h文件,必须导入QtCore模块。




猜你喜欢

转载自blog.csdn.net/kpengk/article/details/79873110