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编码),具体实现代码如下:
另,Qt实现QString转为string的代码如下:#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; }
// 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模块。